我对数据库交互方面的最佳实践感到好奇。我一直在使用一种模式,我相信它可以确保在完成它们之后关闭所有适当的对象。然而,一位同事最近重新编写了我的代码,其中包括“确保我们始终关闭数据库对象”的注释。由于某种原因,我需要知道一种模式是否比另一种模式“更好”。我以某种方式错误使用的模式是什么?一种模式比另一种模式有优势吗?
我一直关注的模式:
public void doStuff() {
try {
final Connection connection = this.getConnection();
try {
final PreparedStatement ps = connection.prepareStatement("SELECT COLA, COLB FROM TBL WHERE COLC = ?");
try {
ps.setString(1, "asdf");
final ResultSet rs = ps.executeQuery();
try {
// get data from rs
} finally {
rs.close();
}
} finally {
ps.close();
}
} finally {
connection.close();
}
} catch (SQLException e) {
// do something with the error
}
}
我的同事修改我的代码的模式:
public void doStuff() {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = this.getConnection();
ps = connection.prepareStatement("SELECT COLA, COLB FROM TBL WHERE COLC = ?");
ps.setString(1, "asdf");
rs = ps.executeQuery();
// get data from rs
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// do something with the error
}
}
if (ps!= null) {
try {
ps.close();
} catch (SQLException e) {
// do something with the error
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// do something with the error
}
}
}
}
答案 0 :(得分:5)
如果您使用的是Java 6或更早版本,请使用后者,因为它更易于阅读和维护。请注意,后者可以通过一些重构来改进,以便在每次调用try-catch
方法时处理繁琐的close
。
如果您使用的是Java 7或更高版本,请使用try-with-resources
:
try (Connection con = ...;
PreparedStatement pstmt = ...) {
pstmt.setXyz(...);
ResultSet rs = pstmt.executeQuery();
//read data from resultset
//and then close it
rs.close();
} catch (Exception e) {
//handle the exception properly...
}
如果您想确保关闭ResultSet
,可以使用嵌套try-with-resources
:
try (Connection con = ...;
PreparedStatement pstmt = ...) {
pstmt.setXyz(...);
try(ResultSet rs = pstmt.executeQuery()) {
//read data from resultset
}
} catch (Exception e) {
//handle the exception properly...
}
答案 1 :(得分:1)
后者更容易阅读;深层筑巢很难说。
我更喜欢closeables周围的安全包装器,例如,如果closeable为null,它们什么都不做。这也使主线代码更容易阅读。
当然,Luigi的回答在Java 7中最有意义。答案 2 :(得分:0)
将数据库资源的关闭抽象为专用的管理器对象通常更简单,更简洁,该对象将包含任何可能被抛出的NPE等。
作为开源项目OpenFire的一部分存在一个写得很好的文章:
此DbConnectionManager中的示例辅助方法:
public static void closeResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
}
catch (SQLException e) {
Log.error(e.getMessage(), e);
}
}
}
因此,在finally
块中,您只需将资源传递回管理器,它就会处理丑陋的逻辑以测试空值并捕获异常等。
像:
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
ps = con.prepareStatement(yourStatement);
rs = ps.executeQuery();
if (rs != null) {
while (rs.next()) {
// do stuff
}
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
} finally {
DbConnectionManager.closeConnection(rs, ps, con);
}
答案 3 :(得分:-5)
模拟Go的延迟声明:D
try(Defer defer = new Defer())
{
Connection connection = ...;
defer.add( connection::close );
....
Path tmpFile = ...;
defer.add( ()->Files.delete(tmpFile) );
....
} // Defer.close() => executing registered actions, from last to first
如何实施Defer
留给读者的练习:)