如何正确关闭资源
当我清理一些代码时,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来处理可能出现的所有问题