连接关闭时,ResultSet没有关闭吗?

时间:2020-03-06 14:27:03  来源:igfitidea点击:

我一直在对我们的一个宠物项目进行代码审查(通常使用诸如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。

关闭连接时,是否关闭了ResultSets和Statements是否有麻烦?

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

解决方案

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

"ORA-01000: maximum open cursors exceeded"

因此:始终关闭ResultSet!

在这种情况下,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)等,而不必担心应该关闭扫描的异常。

我肯定已经见到未封闭的ResultSets存在问题,一直关闭它们会对我造成什么伤害,对吗?需要记住要这样做的不可靠性是转移到为我们管理这些详细信息的框架的最佳原因之一。在开发环境中这可能不可行,但是使用Spring来管理JPA事务我很幸运。打开连接,语句,结果集以及编写过于复杂的try / catch / finally块(finally块中包含try / catch块!)以再次关闭它们的混乱细节就消失了,实际上我们要完成一些工作。我强烈建议我们迁移到这种解决方案。

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

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

通常,如果对象上有close()或者destroy()方法,则有理由要调用它,而忽略它则要自己承担风险。

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

ODBC桥可能会因某些ODBC驱动程序而导致内存泄漏。

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

  • 你知道你有没有好的车手?
  • 将来会使用其他JDBC驱动程序吗?

最佳实践是关闭所有内容。

我们应该始终明确关闭所有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) {
        }
    }
}

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

  • 用户会请求页面
  • 服务器连接到数据库1
  • 服务器在DB 1上选择
  • 服务器"关闭"与DB 1的连接
  • 服务器连接到DB 2
  • 陷入僵局!

发生这种情况的原因有两个,我们遇到的通信量比正常情况高得多,并且默认情况下,J2EE Spec并不会真正关闭连接,直到线程完成执行。因此,在上面的示例中,步骤4从未真正关闭连接,即使它们在最后正确关闭了也是如此。

要解决此问题,我们必须将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>