如何在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; } }