连接关闭时ResultSet未关闭?

时间:2008-09-19 17:35:36

标签: java jdbc findbugs

我一直在对我们的宠物项目之一进行代码审查(主要使用像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();
}

但我在许多项目中遇到过上述模式(来自不少公司),没有人关闭结果集或声明。

关闭Connection时,您是否遇到没有关闭ResultSet和Statements的问题?

我发现只有this,它指的是Oracle在关闭Connections时关闭ResultSet的问题(我们使用Oracle db,因此我的更正)。 java.sql.api在Connection.close()javadoc中没有说什么。

8 个答案:

答案 0 :(得分:51)

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

答案 1 :(得分:28)

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

"ORA-01000: maximum open cursors exceeded"

所以:总是关闭你的ResultSet!

答案 2 :(得分: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) {
        }
    }
}

答案 3 :(得分: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)等,而不必担心关闭扫描的异常会抛出

答案 4 :(得分:8)

ODBC Bridge可能会因某些ODBC驱动程序而产生内存泄漏。

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

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

最佳做法是关闭它。

答案 5 :(得分:8)

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

  1. 用户会请求页面
  2. 服务器连接到DB 1
  3. 服务器在DB 1上选择
  4. 服务器“关闭”与DB 1的连接
  5. 服务器连接到DB 2
  6. 僵持!
  7. 出现这种情况有两个原因,我们遇到的流量远远高于正常情况,默认情况下,J2EE规范实际上并没有关闭您的连接,直到线程完成执行。因此,在上面的例子中,第4步实际上并没有实际关闭连接,即使它们最终被正确关闭了。

    要解决此问题,您必须在web.xml中使用资源引用作为数据库连接,并且必须将res-sharing-scope设置为unharable。

    示例:

    <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>
    

答案 6 :(得分:4)

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

答案 7 :(得分:4)

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

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

一般情况下,如果某个对象上有close()或destroy()方法,则有理由对其进行调用,并且忽略它是由您自己承担的。