连接关闭时ResultSet未关闭?

jb.*_*jb. 49 java findbugs jdbc

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

错误是此代码可能不会释放资源.我发现ResultSet和Statement没有关闭,所以我最后关闭了它们:

finally{
    try{
        rs.close()
    }catch(SqlException se){
        //log it
    }
    try{
        stmt.close();
    }catch(SqlException se){
        //log it
    }
    conn.close();
}
Run Code Online (Sandbox Code Playgroud)

但是我在很多项目中遇到过上述模式(来自不少公司),没有人关闭ResultSet或Statements.

在Connection关闭时,您是否遇到没有关闭ResultSet和Statements的麻烦?

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

Aar*_*ron 52

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

  • Connection#close方法的javadoc表示"立即释放此Connection对象的数据库和JDBC资源".我认为问题是一些不好的实现不能做正确的工作.当池没有关闭相关资源时,他们做错了吗? (14认同)
  • @Ryan Fernandes:嗯,有些池只给你一个connectionProxy对象,它保存所有打开的语句.当它在池中返回时,它会关闭所有打开的语句. (4认同)
  • 大多数主要供应商的JDBC驱动程序都符合规范.但是,大多数(如果不是全部)应用程序服务器都维护一个连接池.它们包装本机连接和重新实现方法,如close(),以便连接可以"合并".这意味着如果您在这些环境中工作,您必须自己关闭语句和结果集之类的资源. (2认同)

neu*_*242 28

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

"ORA-01000: maximum open cursors exceeded"
Run Code Online (Sandbox Code Playgroud)

所以:始终关闭ResultSet!


小智 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) {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 伙计,也许我们需要一个Closable界面,对吧? (6认同)
  • 关闭Statement时,ResultSet会自动关闭.(参见JavaDoc http://download.oracle.com/javase/1.4.2/docs/api/java/sql/Statement.html) (2认同)

Joh*_*ner 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)等,而不必担心关闭扫描的异常会抛出.


Hor*_*ux7 8

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

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

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

最好的做法是关闭它.


Kon*_*rad 8

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

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

出现这种情况有两个原因,我们遇到的流量远远高于正常情况,默认情况下,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>
Run Code Online (Sandbox Code Playgroud)