Java 当我需要在关闭ResultSet之前从ResultSet中填充时,如何提取JDBC代码?
我正在开发一个Java应用程序,它对一个数据库执行许多JDBC查询,每个查询都是用一个单独的方法指定的,格式大致如下Java 当我需要在关闭ResultSet之前从ResultSet中填充时,如何提取JDBC代码?,java,jdbc,Java,Jdbc,我正在开发一个Java应用程序,它对一个数据库执行许多JDBC查询,每个查询都是用一个单独的方法指定的,格式大致如下 public static void sampleJDBCQuery(String query, DBUtil dbUtil, DataStructure dataStructure) { ResultSet rs = null; Handle handle = null; Connection conn = null; Statement stm
public static void sampleJDBCQuery(String query, DBUtil dbUtil, DataStructure dataStructure) {
ResultSet rs = null;
Handle handle = null;
Connection conn = null;
Statement stmt = null;
LOGGER.debug("Executing query = {}", query);
try {
handle = dbUtil.getConnectionHandle();
conn = handle.getConnection();
if (conn != null) {
stmt = conn.createStatement();
if (stmt != null) {
rs = stmt.executeQuery(query);
if (rs != null) {
while (rs.next()) {
// populate dataStructure using rs
}
}
}
}
} catch (Exception e) {
Metrics.markMeter("vertica.read.error");
LOGGER.error(e.getMessage(), e);
} finally {
DBUtil.closeResultSet(rs);
DBUtil.closeStatement(stmt);
DBUtil.close(handle);
LOGGER.debug("Finished query = {}", query);
}
}
有了上述格式的许多不同的示例查询(和数据结构类),我的代码库已经大幅增长。我的目标是提供一个助手方法,为我抽象出大部分JDBC逻辑。我的第一个想法是有一个具有以下签名的方法
public static ResultSet executeJDBCQuery(String query, DBUtil dbUtil)
然后我可以在ResultSet
的行上循环,并为每一行填充相关的数据结构。问题是,我仍然必须关闭返回的ResultSet
,而用另一种方法关闭ResultSet
似乎是一种糟糕的设计
我想我想要的可能是类似于Python的函数装饰器概念的东西,这样我就可以“装饰”JDBC查询,以处理上面sampleJDBCQuery
中的大部分样板文件。如何实现这一点?您可以传入一个策略,告诉方法如何将行映射到对象上的字段
spring jdbc就是这样做的,它定义了:
公共接口行映射器{
T mapRow(ResultSet ResultSet,int rowNum)抛出SQLException;
}
以下是更改方法的方法,包括合并行映射器:
public static <T> List<T> queryList(String query, Connection conn, RowMapper<T> rowMapper) throws SQLException {
LOGGER.debug("Executing query = {}", query);
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
List<> list = new ArrayList<>();
while (rs.next()) {
list.add(rowMapper.mapRow(rs, list.size() + 1));
}
return list;
} finally {
DBUtil.closeResultSet(rs);
DBUtil.closeStatement(stmt);
LOGGER.debug("Finished query = {}", query);
}
}
publicstaticlist queryList(字符串查询、连接连接、行映射器行映射器)抛出SQLException{
debug(“正在执行查询={}”,查询);
试一试{
语句stmt=conn.createStatement();
ResultSet rs=stmt.executeQuery(查询);
列表=新的ArrayList();
while(rs.next()){
add(rowMapper.mapRow(rs,list.size()+1));
}
退货清单;
}最后{
DBUtil.closeResultSet(rs);
DBUtil.closeStatement(stmt);
debug(“完成的查询={}”,查询);
}
}
在这里捕获所有异常并不好,因为如果出现问题,您希望能够使用异常退出当前操作。否则,您将在日志中有多个错误堆栈跟踪,一个在此处发生错误,另一个在下游发生错误,当您的代码期望出现一个不是由于前一个错误导致的结果时,代码将失败。当第一个错误发生时,快速失败
下一步是参数化查询,这样就不必将参数值括在引号中或担心sql注入。有关SpringJDBC如何处理此问题的示例,请参见
我将连接内容从方法中移出;传入连接允许在同一个JDBC本地事务中执行多个sql语句。(连接仍然需要关闭,这是一个错误的地方。)
在此传递此句柄也违反了:
特别是,对象应该避免调用另一个方法返回的成员对象的方法。对于许多使用一个点作为字段标识符的现代面向对象语言来说,这个法则可以简单地表述为“只使用一个点”。也就是说,代码a.b.Method()违反了法律,而a.Method()则没有。作为一个类比,当一个人想要一只狗走路时,他不会命令狗的腿直接走路;取而代之的是,一个人命令狗,然后狗命令它自己的腿
即使您只做只读的工作,让查询共享事务也可以提高一致性并提高性能。使用事务对Spring来说不那么痛苦,您可以使用注释以声明方式实现事务,以显示边界的位置
这里的大局答案是:最好采用一个预先存在的工具(SpringJDBC或类似工具),它们已经解决了您甚至还没有考虑的问题,而不是零碎地重新设计它,这显然是您要走的路 也许您需要的是某种委托模式,在这种模式中,您使用一个方法定义一个通用接口,该方法将ResultSet作为参数,并从中提取当前值(我这样做的目的是委托只提取“当前”值)行,通过行进行集成的责任仍然是调用方法的责任)所有这些空检查都应该是完全不必要的;这些方法永远不应该返回null
(可能除了handle.getConnection
,但是如果它可以返回null
,那么你就应该解决这个问题)。如果没有强制使用JDBC的要求,在重新发明轮子之前,使用类似Hibernate的ORM。Hibernate在某些方面很好。但它有一个学习曲线,可以使事情变得更加模糊。当然可以,看看,但不是每个人都可以。
public static <T> List<T> queryList(String query, Connection conn, RowMapper<T> rowMapper) throws SQLException {
LOGGER.debug("Executing query = {}", query);
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
List<> list = new ArrayList<>();
while (rs.next()) {
list.add(rowMapper.mapRow(rs, list.size() + 1));
}
return list;
} finally {
DBUtil.closeResultSet(rs);
DBUtil.closeStatement(stmt);
LOGGER.debug("Finished query = {}", query);
}
}