连接closures时ResultSet没有closures?

我一直在做我们的宠物项目之一的代码审查(主要是使用像FindBugs的工具)和FindBugs标记下面的代码是错误的(伪代码):

Connection conn = dataSource.getConnection(); try{ PreparedStatement stmt = conn.prepareStatement(); //initialize the statement stmt.execute(); ResultSet rs = stmt.getResultSet(); //get data }finally{ conn.close(); } 

错误是这个代码可能不会释放资源。 我发现ResultSet和Statement没有closures,所以最后closures了它们:

 finally{ try{ rs.close() }catch(SqlException se){ //log it } try{ stmt.close(); }catch(SqlException se){ //log it } conn.close(); } 

但是我在许多项目(来自不less公司)遇到过上述模式,没有人closuresResultSet或Statements。

当Connectionclosures时,您是否遇到ResultSet和Statement没有closures的问题?

我发现只有这一点 ,它指的是甲骨文在closures连接(我们使用Oracle数据库,因此我的更正)closures结果集有问题。 java.sql.api在Connection.close()javadoc中什么都没说。

只closures连接而不closures结果集的一个问题是,如果你的连接pipe理代码使用连接池,那么connection.close()就会把连接放回池中。 此外,某些数据库在服务器上有一个游标资源,除非明确closures,否则不会被正确释放。

即使连接已closures,我在Oracle中未结束的ResultSets也遇到了问题。 我得到的错误是

 "ORA-01000: maximum open cursors exceeded" 

所以:总是closures你的ResultSet!

您应该始终closures所有JDBC资源。 正如Aaron和John所说,closures连接通常只会将其返回到池中,而不是所有的JDBC驱动程序都以相同的方式实现。

这是一个可以从finally块使用的实用方法:

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

在这种情况下,Oracle会给你打开游标的错误。

根据: http : //java.sun.com/javase/6/docs/api/java/sql/Statement.html

它看起来像重用一个语句将closures任何打开的结果集,closures语句将closures任何结果集,但我没有看到任何closures连接将closures它创build的任何资源。

所有这些细节都留给了JDBC驱动程序提供者。

它总是最安全的明确closures一切。 我们编写了一个util类,用try {xxx} catch(Throwable {})封装所有的东西,这样你就可以调用Utils.close(rs)和Utils.close(stmt)等等,而不用担心closures扫描的exception。

ODBC桥可以通过一些ODBC驱动程序产生内存泄漏。

如果您使用了一个好的JDBC驱动程序,那么closures连接应该没有任何问题。 但是有两个问题:

  • 你知道你是否有一个好的司机?
  • 您将来会使用其他JDBC驱动程序吗?

最好的做法是closures一切。

我在一个大的J2EE Web环境中工作。 我们有几个数据库可以连接到一个请求。 我们在一些应用程序中开始出现逻辑死锁。 问题是如下:

  1. 用户会请求页面
  2. 服务器连接到DB 1
  3. 服务器在DB 1上select
  4. 服务器“closures”到DB 1的连接
  5. 服务器连接到数据库2
  6. 僵持!

发生这种情况的原因有两个,一是stream量远高于正常情况,而另一方面J2EE Spec在默认情况下并没有真正closures连接,直到线程完成执行。 所以,在上面的例子中,第4步从来没有真正closures连接,即使它们在最后被正确closures。

要解决这个问题,你必须在web.xml中为你的数据库连接使用资源引用,你必须把res-sharing-scope设置为不可用。

例:

 <resource-ref> <description>My Database</description> <res-ref-name>jdbc/jndi/pathtodatasource</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Unshareable</res-sharing-scope> </resource-ref> 

我肯定看到了未closures的ResultSets的问题,closures它们总是会有什么伤害,对吧? 需要记住这一点的不可靠性是转移到为您pipe理这些细节的框架的最佳理由之一。 在您的开发环境中可能不太可行,但是使用Spring来pipe理JPA事务已经非常幸运了。 打开连接,语句,结果集以及编写过度复杂的try / catch / finally块(在finally块中带有try / catch块)的混乱细节会再次closures它们,结果只会让你实际完成一些工作。 我强烈build议迁移到这种解决scheme。

在Java中,语句(而不是结果集)与Oracle中的游标相关联。 最好closures打开的资源,因为JVM和系统资源可能会发生意外的行为。

此外,一些JDBC池框架池语句和连接,所以不closures它们可能不标记这些对象在池中是免费的,并导致框架中的性能问题。

一般来说,如果对象上有一个close()或destroy()方法,那么有一个理由来调用它,而忽略它是在你自己的危险中完成的。