确保数据库对象关闭的首选方法是什么?
What is the preferred method of ensuring that database objects are closed?
我很好奇有关数据库交互的最佳做法。我一直在使用一种模式,我相信这种模式可以确保在我处理完所有适当的对象后关闭它们。然而,一位同事最近用 "making sure we always close database objects" 的注释重构了我的代码。由于某种原因,我需要知道一种模式是否比另一种模式 "better"。我一直在使用的模式是否有问题?一种模式比另一种模式有优势吗?
我一直遵循的模式:
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
}
}
}
}
如果您使用的是 Java 6 或更早版本,请使用后者,因为它更易于阅读和维护。请注意,后者可以通过一些重构来改进,以处理每次调用 close
方法时繁琐的 try-catch
。
如果您使用的是 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...
}
后者更易读;深层嵌套很难推理。
我更喜欢可关闭对象的安全包装器,例如,如果可关闭对象为空,它们什么也不做。这也使得主线代码更易于阅读。
Luigi 的回答当然从 Java 7 开始最有意义。
将数据库资源的关闭抽象为专用的管理器对象通常更简单、更清晰,该对象将包含任何 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);
}
模拟Go的defer语句: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
留给读者作为练习:)
我很好奇有关数据库交互的最佳做法。我一直在使用一种模式,我相信这种模式可以确保在我处理完所有适当的对象后关闭它们。然而,一位同事最近用 "making sure we always close database objects" 的注释重构了我的代码。由于某种原因,我需要知道一种模式是否比另一种模式 "better"。我一直在使用的模式是否有问题?一种模式比另一种模式有优势吗?
我一直遵循的模式:
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
}
}
}
}
如果您使用的是 Java 6 或更早版本,请使用后者,因为它更易于阅读和维护。请注意,后者可以通过一些重构来改进,以处理每次调用 close
方法时繁琐的 try-catch
。
如果您使用的是 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...
}
后者更易读;深层嵌套很难推理。
我更喜欢可关闭对象的安全包装器,例如,如果可关闭对象为空,它们什么也不做。这也使得主线代码更易于阅读。
Luigi 的回答当然从 Java 7 开始最有意义。
将数据库资源的关闭抽象为专用的管理器对象通常更简单、更清晰,该对象将包含任何 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);
}
模拟Go的defer语句: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
留给读者作为练习:)