finally { if (con != null) { con.close(); } if (stmt != null) { stmt.close(); } }在这段代码中, 关闭资源时,出现了次序错误
而有关多个异常的处理的代码绝大多数是按照下面的结构实现
try { } catch (SQLException e) { } catch (IOException e) { } catch (XXXException e) { } finally { }
如果没有打开数据库连接事务,代码可能是这样的:
try { ... } catch (SQLException e) { } catch (IOException e) { } catch (XXXException e) { } finally { con.close() }在这种情况下,数据库发生死锁的可能性为零。
try { ... con.commit(); } catch (SQLException e) { con.rollback(); } catch (IOException e) { } catch (XXXException e) { } finally { con.close(); }在这样混合了多种异常的代码中,如果发生了非SQLException异常,极有可能出现死锁的情况,一旦发生非SQLException异常,事务既没有提交,也没有回滚,虽然JDBC关闭了数据库连接,但是在数据库服务器中,数据库事务仍然是打开的。在使用SQL Server2000时,这一点可以通过对SQL Server2000的企业管理器/管理/当前活动/锁/对象等部分观察而得到结论。其它的数据库产品是否也有类似的情况?这段代码看似优雅,但缺乏健壮性;从代码的健壮性考虑,应当将代码拆分成三个try{}catch(XXXException e){}finally{}。如果希望维持代码的优雅,则应当在这段代码的实现细节上多下点功夫,比如多增加几个boolean变量,看代码能否执行到下一个阶段。
try{ ... } catch (java.lang.ClassCastException ex) { throw new SQLException("java.lang.ClassCastException: " + ex.getMessage(), ex.getCause()); } catch (NumberFormatException ex) { throw new SQLException("NumberFormatException: " + ex.getMessage(), ex.getCause()); } catch (IOException ex) { throw new SQLException("IOException: " + ex.getMessage(), ex.getCause()); } finally { pstmt.close(); }相关的资源打开和关闭,分散在整个ProcessVO的多个方法中,但在关闭资源时,严格注意次序。 对于数据库连接(或者叫会话)、事务、PreparedStatement对象、Statement对象、ResultSet对象等资源,ProcessVO中遵守的次序是: 先打开,后关闭,后打开,先关闭。
/** * 提交事务。 * * @throws SQLException */ public void commit() throws SQLException { if (!con.isClosed()) { if (!autoCommit) { try { con.commit(); autoCommit = true; con.setAutoCommit(autoCommit); failCommit = false; con.close(); } finally { if (failCommit) { rollback(); } } } } }
/** * 事务回滚。 * * @throws SQLException */ public void rollback() throws SQLException { if (!con.isClosed()) { if (!this.autoCommit) { if (failCommit) { if (savepoint == null) { con.rollback(); con.close(); System.out.println("[" + dt.dateTime() + "] SQLServerException. Connection is already rollback and closed ."); } else { con.rollback(savepoint); con.close(); System.out.println("[" + dt.dateTime() + "] SQLServerException. Connection is already rollback at " + savepoint + " point and closed ."); } autoCommit = true; } } } }
/** * 关闭数据库联接 * * @throws SQLException */ @Override public void closeCon() throws SQLException { try { /* * if (rs != null && !rs.isClosed()) { rs.close(); } * * if (stmt != null && !stmt.isClosed()) { stmt.close(); } */ } finally { if (con != null && !con.isClosed()) { con.close(); } } }
/** * 终结守卫者,最后一道安全屏障,关闭联接。开发人员不应依赖此方法来关闭联接,但以下情况发生后,可能造成数据库连接不能关闭,因此有必要保留终结守卫者模式。长期的实践证明,终结守卫者能起到良好的作用。<br/> * 1、网络连接终止;<br/> 2、应用程序中发生非SQLException异常后,可能导致数据库连接没有关闭。<br/> */ private final Object _finalizerGuardian = new Object() { @Override protected void finalize() throws SQLException, Throwable { if (con != null && !con.isClosed()) { try { super.finalize(); } finally { if (!autoCommit) { if (failCommit) { try { if (savepoint == null) { con.rollback(); } else { con.rollback(savepoint); } System.out.println("[" + dt.dateTime() + "] Connection rollback in finalize."); } catch (SQLException ex) { Logger.getLogger(ProcessVO.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); } finally { con.close(); autoCommit = true; System.out.println("[" + dt.dateTime() + "] Connection is not autocommit, rollback and then closed in finalize."); } } else { con.close(); System.out.println("[" + dt.dateTime() + "] Connection is autocommit, closed in finalize,You should close it."); } } else { con.close(); System.out.println("[" + dt.dateTime() + "] Connection closed in finalize,You should close it."); } } } } };