如何在Java中正确清理JDBC资源?

清理JDBC资源时最佳做法是什么?为什么? 我保持示例简短,因此只是清理ResultSet。

finally { if(rs != null) try{ rs.close(); } catch(SQLException ignored) {} } 

 finally { try{ rs.close(); } catch(Exception ignored) {} } 

我个人赞成第二种选择,因为它有点短。 对此的任何意见都非常感谢。

如今,JDK 7为您提供了清理资源的最简单方法:

 String query = "select COF_NAME, PRICE from COFFEES"; try (Statement stmt = con.createStatement()) { ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String coffeeName = rs.getString("COF_NAME"); float price = rs.getFloat("PRICE"); System.out.println(coffeeName + ", " + price); } } 

try语句确保在语句结束时关闭每个资源。 请参阅http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

正如其他人所指出的,JDBC资源(语句,结果集等)很少是null 。 如果是,那么你手上的问题比NullPointerException 更大 。 在这方面, NullPointerException将帮助提醒您JDBC驱动程序的严重问题。 如果你的JDBC驱动程序实际上为你提供了null引用,那么在调用close()之前典型的null检查会无声地隐藏问题。

同样,并非所有JDBC驱动程序都严格遵循规范。 例如,某些驱动程序在关闭Statement时不会自动关闭ResultSet 。 因此,您必须确保显式关闭ResultSet及其Statement (叹气)。

在实践中,我发现这种技术很有用(尽管它不是最漂亮的):

 PreparedStatement statement = connection.prepareStatement("..."); try { ResultSet results = statement.executeQuery(); try { while (results.next()) { // ... } } finally { results.close(); } } finally { statement.close(); } 

此技术保证执行每个close()语句,从ResultSet开始并向外工作。 NullPointerException驱动程序为您提供null引用,仍会抛出NullPointerException ,但我允许这样做是出于开头所解释的原因。 如果任何close()语句失败,仍会抛出SQLException (我认为这是件好事 – 我想知道是否出错)。

我发现你的第二个(不常见)版本没问题。

  • 通常,rs不会为null ,因此在极少数情况下会发生NPE。 所以我认为这里没有性能问题。
  • rs = null情况下,两个版本的行为完全相同

唯一的缺点 – 如果我们要关闭多个资源,那么我们必须为每个资源添加一个try / catch,如果我们想要关闭尽可能多的资源。 否则,我们将使用第一个null输入catch子句,这可能会导致未标记的泄漏。

所以看起来像这样:

 finally { try{rs.close(); }catch(Exception ignored){} try{stmt.close();}catch(Exception ignored){} try{conn.close();}catch(Exception ignored){} } 

…这仍然是可读和可理解的。 但是,根据从不改变常见的模式 – 我会坚持使用老式的方法首先测试null并在关闭时捕获SQLException

我倾向于使用以下方法。 我认为检查null是好的,因为它显示了你的意图,即你确实意识到这些对象在极少数情况下可能为空。 (空检查也比创建NullPointerException更快。)我也认为记录exception是好的,而不是吞下它们。 在close失败的情况下,我想了解它并将其存储在我的日志文件中。

 finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { LOG.warn("Failed to close rs", e); } } if (st != null) { try { st.close(); } catch (SQLException e) { LOG.warn("Failed to close st", e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { LOG.warn("Failed to close conn", e); } } } 

如果您要经常这样做,而不是一遍又一遍地复制和粘贴此代码,请使用静态方法创建一个实用程序类来关闭ResultSet,Statement和Connection。

使用DBUtils,您可以非常简洁地执行此清理,如下所示:

  finally { DBUtils.closeQuietly(rs); DBUtils.closeQuietly(st); DBUtils.closeQuietly(conn); } 
 public static void close(Statement... statements) { for (Statement stmt : statements) { try { if (stmt != null) stmt.close(); } catch (SQLException se) { }// nothing we can do } } public static void close(Connection conn) { try { if (conn != null) conn.close(); } catch (SQLException se) { }// nothing we can do } public static void close(ResultSet rs) { try { if (rs != null) rs.close(); } catch (SQLException se) { }// nothing we can do } 

这是我对JDK 6的方法。如果你有JDK 7+,你最好使用我在这里描述的方法https://stackoverflow.com/a/9200053/259237

 private void querySomething() { Connection connection = null; PreparedStatement statement = null; ResultSet rs = null; try { // get connection // prepare statement // execute query // and so on } catch (SQLException e) { throw new MyException("Error while talking to database", e); } finally { close(connection, statement, rs); } } // useful because you probably have more than one method interacting with database public static void close (Connection connection, Statement statement, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (Exception e) { _logger.warning(e.toString()); } } if (statement != null) { try { statement.close(); } catch (Exception e) { _logger.warning(e.toString()); } } if (connection != null) { try { connection.close(); } catch (Exception e) { _logger.warning(e.toString()); } } } 
  • 它很短。
  • 它定义了一个可以静态导入的close方法。
  • 它避免了空的挡块。
  • 它处理可能发生的任何SQLException(即使在getConnection或close方法中)。
  • 这是无效的。
 ResultSet rs = //initialize here try { // do stuff here } finally { try { rs.close(); } catch(SQLException ignored) {} } 

如果您正在编写长时间运行的应用程序,则应考虑连接池。

Apache DBCP项目为您完成了很多这项工作。 您还可以查看类似Spring JDBC或Hibernate的内容。

Spring的东西使用了对象池,并添加了一些非常好的方法来抽象出JDBC的肮脏。

 protected void closeAll(){ closeResultSet(); closeStatement(); closeConnection(); } protected void closeConnection(){ if (connection != null) { try { connection.close(); } catch (SQLException e) { /*Logger*/ } connection = null; } } protected void closeStatement() { if (stmt != null) { try { ocstmt.close(); } catch (SQLException e) { /*Logger*/ } ocstmt = null; } } protected void closeResultSet() { if (rs != null) { try { rs.close(); } catch (SQLException e) { /*Logger*/ } rs = null; } }