49

我一直在对我们的一个宠物项目进行代码审查(主要使用 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 没有关闭,所以我最终将它们关闭:

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

但是我在很多项目(来自不少公司)中都遇到了上述模式,并且没有人关闭 ResultSets 或 Statements。

当连接关闭时,您是否遇到过结果集和语句未关闭的问题?

我只发现了这个,它指的是 Oracle 在关闭连接时遇到关闭 ResultSets 的问题(我们使用 Oracle db,因此我进行了更正)。java.sql.api 在 Connection.close() javadoc 中什么也没说。

4

8 回答 8

52

仅关闭连接而不关闭结果集的一个问题是,如果您的连接管理代码正在使用连接池,则只connection.close()会将连接放回池中。此外,某些数据库在服务器上有游标资源,除非显式关闭,否则无法正确释放。

于 2008-09-19T18:00:44.100 回答
29

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

"ORA-01000: maximum open cursors exceeded"

所以:总是关闭你的结果集!

于 2008-09-19T17:42:14.940 回答
19

您应该始终明确关闭所有 JDBC 资源。正如 Aaron 和 John 已经说过的,关闭连接通常只会将其返回到池中,并且并非所有 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) {
        }
    }
}
于 2008-09-19T19:32:00.980 回答
9

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

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

看起来重用语句将关闭任何打开的结果集,关闭语句将关闭任何结果集,但我没有看到任何关于关闭连接会关闭它创建的任何资源的信息。

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

明确关闭所有内容总是最安全的。我们编写了一个 util 类,它使用 try{ xxx } catch (Throwable {} 包装所有内容,这样您就可以调用 Utils.close(rs) 和 Utils.close(stmt) 等,而不必担心关闭扫描可能会抛出的异常.

于 2008-09-19T17:50:15.297 回答
8

ODBC 桥可以使用某些 ODBC 驱动程序产生内存泄漏。

如果您使用良好的 JDBC 驱动程序,那么关闭连接应该不会有任何问题。但是有2个问题:

  • 你知道你是否有一个好司机吗?
  • 以后还会使用其他的 JDBC 驱动吗?

最好的做法是全部关闭。

于 2008-09-19T18:53:01.137 回答
8

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

  1. 用户会请求页面
  2. 服务器连接到 DB 1
  3. DB 1 上的服务器选择
  4. 服务器“关闭”与 DB 1 的连接
  5. 服务器连接到 DB 2
  6. 僵局!

发生这种情况有两个原因,我们遇到的流量远高于正常情况,并且默认情况下 J2EE 规范实际上不会关闭您的连接,直到线程完成执行。因此,在上面的示例中,步骤 4 从未真正关闭连接,即使它们在 finally 中正确关闭。

要解决此问题,您必须为数据库连接使用 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>
于 2008-09-19T20:07:18.947 回答
4

我肯定看到了未关闭的 ResultSets 的问题,并且一直关闭它们会有什么伤害,对吧?需要记住这样做的不可靠性是迁移到为您管理这些细节的框架的最佳理由之一。这在您的开发环境中可能不可行,但我很幸运使用 Spring 来管理 JPA 事务。打开连接、语句、结果集和编写过于复杂的 try/catch/finally 块(在 finally 块中使用 try/catch 块)以再次关闭它们的杂乱细节消失了,让您真正完成一些工作. 我强烈建议迁移到这种解决方案。

于 2008-09-19T17:56:19.587 回答
4

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

此外,一些 JDBC 池框架池语句和连接,因此不关闭它们可能不会将这些对象标记为池中的空闲,并导致框架中的性能问题。

通常,如果对象上有 close() 或 destroy() 方法,则有理由调用它,而忽略它会造成后果自负。

于 2008-09-19T18:00:03.650 回答