考虑一下代码:
PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.createStatement(myQueryString); rs = ps.executeQuery(); // process the results... } catch (java.sql.SQLException e) { log.error("an error!", e); throw new MyAppException("I'm sorry. Your query did not work."); } finally { ps.close(); rs.close(); }
上面没有编译,因为都PreparedStatement.close()
和ResultSet.close()
抛出java.sql.SQLException
.那么我要在finally子句中添加一个try/catch块吗?或者将close语句移动到try子句中?或者只是不打扰打电话?
在Java 7中,不应显式关闭它们,而应使用自动资源管理来确保关闭资源并正确处理异常.异常处理的工作方式如下:
Exception in try | Exception in close | Result -----------------+--------------------+---------------------------------------- No | No | Continue normally No | Yes | Throw the close() exception Yes | No | Throw the exception from try block Yes | Yes | Add close() exception to main exception | | as "suppressed", throw main exception
希望这是有道理的.允许漂亮的代码,像这样:
private void doEverythingInOneSillyMethod(String key) throws MyAppException { try (Connection db = ds.getConnection()) { db.setReadOnly(true); ... try (PreparedStatement ps = db.prepareStatement(...)) { ps.setString(1, key); ... try (ResultSet rs = ps.executeQuery()) { ... } } } catch (SQLException ex) { throw new MyAppException("Query failed.", ex); } }
在Java 7之前,最好使用嵌套的finally块,而不是测试null的引用.
我将展示的示例可能看起来很难看深嵌套,但实际上,设计良好的代码可能不会在同一个方法中创建连接,语句和结果; 通常,每个嵌套级别都涉及将资源传递给另一个方法,该方法将其用作另一个资源的工厂.使用这种方法,来自a close()
的异常将掩盖try
块内的异常.这可以克服,但它会导致代码更加混乱,并且需要一个自定义异常类,它提供Java 7中存在的"抑制"异常链.
Connection db = ds.getConnection(); try { PreparedStatement ps = ...; try { ResultSet rs = ... try { ... } finally { rs.close(); } } finally { ps.close(); } } finally { db.close(); }
如果你真的在手动推出自己的jdbc,它肯定会变得混乱.最后的close()需要用自己的try catch包装,这至少是丑陋的.您无法跳过关闭,但是当连接关闭时,资源将被清除(如果您正在使用池,则可能不会立即清除).实际上,使用框架(例如hibernate)来管理数据库访问的一个主要卖点是管理连接和结果集处理,这样你就不会忘记关闭.
你可以做一些像这样简单的事情,至少可以隐藏这些混乱,并保证你不会忘记一些事情.
public static void close(ResultSet rs, Statement ps, Connection conn) { if (rs!=null) { try { rs.close(); } catch(SQLException e) { logger.error("The result set cannot be closed.", e); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { logger.error("The statement cannot be closed.", e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { logger.error("The data source connection cannot be closed.", e); } } }
然后,
finally { close(rs, ps, null); }
对于文件I/O,我通常会在finally块中添加try/catch.但是,您必须小心不要从finally块中抛出任何异常,因为它们将导致原始异常(如果有)丢失.
有关数据库连接关闭的更具体示例,请参阅此文章.
不要浪费时间编写低级异常管理,使用更高级别的API(如Spring-JDBC)或围绕连接/语句/ rs对象的自定义包装器来隐藏凌乱的try-catch循环代码.
另请注意:
"当一个Statement对象关闭时,它的当前ResultSet对象(如果存在)也会关闭."
http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close()
仅在最终关闭PreparedStatement并且仅在它尚未关闭时才足够.如果你想要非常特别,请关闭ResultSet FIRST,而不是在关闭PreparedStatement之后(关闭它之后,像这里的一些示例一样,实际上应该保证异常,因为它已经关闭).
我通常有一个实用方法可以关闭这样的事情,包括注意不要尝试使用空引用做任何事情.
通常如果close()
抛出异常我实际上并不关心,所以我只记录异常并吞下它 - 但另一种选择是将其转换为RuntimeException
.无论哪种方式,我建议在一个易于调用的实用方法中进行,因为您可能需要在很多地方执行此操作.
请注意,如果关闭PreparedStatement失败,您当前的解决方案将不会关闭ResultSet - 最好使用嵌套的finally块.