如何正确关闭资源

当我清理一些代码时,FindBugs向我指出了一些使用Connection,CallableStatement和ResultSet对象的JDBC代码。 这是该代码的一个片段:

CallableStatement cStmt = getConnection().prepareCall("..."); ... ResultSet rs = cStmt.executeQuery(); while ( rs.next() ) { ... } cStmt.close(); rs.close(); con.close(); 

FindBugs指出这些应该在finally块内。 我开始重构我的代码来做这个,我开始想知道如何处理finally块中的代码。

创建CallableStatement的Connection对象可能会抛出exception,将ResultSet对象保留为null。 当我尝试关闭ResultSet时,我会得到一个NullPointerException,而我的Connection将永远不会被关闭。 实际上, 这个线程提出了相同的概念,并表明将close()调用包装在null检查中是个好主意。

但是其他可能的例外呢? 根据Java API规范,如果发生数据库错误,Statement.close()可以抛出SQLException。 因此,即使我的CallableStatement不为null并且我可以成功调用它上的close(),我仍然可能会得到一个exception并且没有机会关闭我的其他资源。

我能想到的唯一“故障安全”解决方案是将每个close()调用包装在自己的try / catch块中,如下所示:

 finally { try { cStmt.close(); } catch (Exception e) { /* Intentionally Swallow Exception */ } try { rs.close(); } catch (Exception e) { /* Intentionally Swallow Exception */ } try { con.close(); } catch (Exception e) { /* Intentionally Swallow Exception */ } } 

男孩,如果那看起来不太可怕。 有没有更好的方法来解决这个问题?

我认为已经提到了最佳答案,但我认为可以提及您可以考虑可自动分解资源的新JDK 7function。

 try{ try(Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/hrdb", "obiwan", "kenobi"); Statement stm = conn.createStatement(); ResultSet rs = stm.executeQuery("select name from department")) { while(rs.next()){ System.out.println(rs.getString("name")); } } }catch(SQLException e){ //you might wanna check e.getSuppressed() as well //log, wrap, rethrow as desired. } 

我们现在并不是所有人都可以迁移到JDK 7,但对于那些可以开始使用开发人员预览版的人来说,这提供了一种有趣的做事方式,当然可能会在不久的将来弃用其他方法。

基本上这就是你所做的,除了你首先不一定吞下exception(你可以null检查并至少LOG例外)。 其次,你可以设置一个很好的实用工具类

 public static void close(ResultSet rs) { try { if (rs != null) rs.close(); } catch (SQLException (e) { log.error("",e); } } 

然后你只需要静态导入该类。

你终于变成了类似的东西

 finally { close(resultset); close(statement); close(connection); } 

这真的不是那么可怕。

如果可以的话,使用Lombok的清理 :

 @Cleanup Connection c = ... @Cleanup statement = c.prepareStatement(...); @Cleanup rs = statement.execute(...); 

这可以转换为三个嵌套的try-finally块,并且可以正常工作。 没有充分理由,永远不要吞下一个例外!

替代:

写一个这样的自己的实用方法:

 public static void close(ResultSet rs, Statement stmt, Connection con) throws SQLException { try { try { if (rs!=null) rs.close(); } finally { if (stmt!=null) stmt.close(); } } finally { if (con!=null) con.close(); } } 

并使用它

 try { Connection con = ... Statement stmt = ... ResultSet rs = ... } finally { close(rs, stmt, con); } 

让Exception冒泡或按你的意愿处理它。

您只需关闭连接。

 try { cStmt.close(); } catch(Exception e) { /* Intentionally Swallow Exception */ } 

来自docs.oracle.com:

当一个Statement对象被垃圾回收时会自动关闭。 关闭Statement对象时,其当前ResultSet对象(如果存在)也将关闭。

在Connection上调用close()会释放其数据库和JDBC资源。

我知道隐藏所有丑陋的try-catch样板代码的唯一方法是使用类似Spring的JBDC模板 。

您可以将一个块包装到另一个块中:

 try{ Connection c = ... try{ statement = c.prepareStatement(...); try{ rs = statement.execute(...); }finally{ rs.close(); } }finally{ statement.close() } }finally{ c.close(); } }catch(SQLException e){} 

使用最下面的catch-block来处理可能出现的所有问题