我正在开发一个Java应用程序,该应用程序对数据库执行许多JDBC查询,每个查询都是在单独的方法中指定的,大致采用以下格式。
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
的行,并为每一行填充相关的DataStructure
。问题是我仍然必须关闭返回的ResultSet
并以另一种方法关闭ResultSet
似乎是不好的设计。
我想我所寻找的可能与Python的函数装饰器概念类似,因此我可以“修饰” JDBC查询以处理上面sampleJDBCQuery
中存在的大多数样板。我该如何实现?
答案 0 :(得分:2)
您可以传入一种策略来告诉该方法如何将行映射到对象上的字段。
这是spring-jdbc所做的,它将RowMapper定义为:
public interface RowMapper<T> {
T mapRow(ResultSet resultSet, int rowNum) throws SQLException;
}
这是更改方法的方法,包括合并rowMapper:
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);
}
}
在此处捕获所有异常不是一件好事,因为如果发生问题,您希望能够使用异常来退出当前操作。否则,您将在日志中有多个错误堆栈跟踪,一个在此处发生错误,然后在另一个下游,您的代码在期望出现并非由先前错误引起的结果时失败。发生第一个错误时,只需快速失败即可。
下一步将是对查询进行参数化,因此您不必在引号中包含参数值或担心sql注入。有关spring-jdbc如何处理此问题的示例,请参见this answer。
我将连接内容从方法中移出了;传递连接允许您在同一JDBC本地事务中执行多个sql语句。 (连接仍然需要关闭,这只是做错了地方。)
在此传递此句柄也违反了the Law of Demeter:
尤其是,对象应避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,可以将法律简单地表述为“仅使用一个点”。也就是说,代码a.b.Method()违反了法律,而a.Method()没有。打个比方,当一个人要一只狗走路时,并不命令狗的腿直接走路;取而代之的是命令狗,然后命令自己的腿。
即使您只做只读工作,让查询共享事务也可以提高一致性并提高性能。在Spring中,使用事务的痛苦较小,您可以使用批注来声明性地实现事务,以显示边界的位置。
这里的全局答案是:最好采用一个预先存在的工具(spring-jdbc或类似的东西),在该工具中,他们已经解决了您甚至还没有考虑的问题,而不是重塑它,这显然是您要走的路。