连接关闭时结果集未关闭?
Posted
技术标签:
【中文标题】连接关闭时结果集未关闭?【英文标题】:ResultSet not closed when connection closed? 【发布时间】:2010-09-11 08:41:32 【问题描述】:我一直在对我们的一个宠物项目进行代码审查(主要使用 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。
当连接关闭时,您是否遇到过结果集和语句未关闭的问题?
我只找到了this,它指的是 Oracle 在关闭连接时遇到关闭 ResultSets 的问题(我们使用 Oracle db,因此我进行了更正)。 java.sql.api 在 Connection.close() javadoc 中什么也没说。
【问题讨论】:
我强烈推荐使用 Apache commons-dbutils (commons.apache.org/dbutils),它是一个轻量级的 JDBC 库,真正清理了很多样板 JDBC 代码。 这是一种当他们不关闭相关对象时遇到的错误 - “ORA-01000: 超出最大打开游标” - ***.com/questions/12192592/… 数据库游标 - ***.com/questions/3861558/… 游标是一种工具,允许您迭代集合中的记录。它有顺序和当前记录的概念。 【参考方案1】:我在 Oracle 中遇到了未关闭的 ResultSets 的问题,即使连接已关闭。我得到的错误是
"ORA-01000: maximum open cursors exceeded"
所以:总是关闭你的 ResultSet!
【讨论】:
+1 - 用于提及不关闭资源的后果。【参考方案2】:只关闭连接而不关闭结果集的一个问题是,如果您的连接管理代码使用连接池,connection.close()
只会将连接放回池中。此外,某些数据库在服务器上有游标资源,除非显式关闭,否则无法正确释放。
【讨论】:
Connection#close 方法的 javadocs 说“立即释放此 Connection 对象的数据库和 JDBC 资源”。我认为问题在于一些糟糕的实现没有做正确的工作。当池不关闭相关资源时,他们做错了吗? 大多数主要供应商的 JDBC 驱动程序都符合规范。但是,大多数(如果不是全部)应用程序服务器维护一个连接池。它们包装本机连接并重新实现 close() 等方法,以便可以“池化”连接。这意味着如果您在这些环境中工作,您必须自己关闭 Statements 和 ResultSets 等资源。 @Ryan Fernandes:嗯,一些池只是给你一个 connectionProxy 对象,它保存所有打开的语句。当它返回到池中时,它会关闭所有打开的语句。 这不是真的。池连接将在返回池之前清理=关闭所有对象,除了与 RDBMS 的套接字连接。【参考方案3】:我在一个大型 J2EE Web 环境中工作。我们有几个数据库可以在一个请求中连接。我们的一些应用程序开始出现逻辑死锁。问题如下:
-
用户会请求页面
服务器连接到 DB 1
DB 1 上的服务器选择
服务器“关闭”与 DB 1 的连接
服务器连接到 DB 2
陷入僵局!
发生这种情况有两个原因,我们遇到的流量远高于正常情况,并且 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>
【讨论】:
【参考方案4】:您应该始终明确关闭所有 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)
【讨论】:
伙计,也许我们需要一个 Closable 接口,嗯? ResultSet 在您关闭语句时自动关闭。 (参见 JavaDoc download.oracle.com/javase/1.4.2/docs/api/java/sql/…) 感谢发帖。我喜欢这个主意。 Connection、Stamement 和 ResultSet 自 Java 7 起都实现了 AutoCloseable。【参考方案5】:ODBC 桥可能会在某些 ODBC 驱动程序中产生内存泄漏。
如果您使用良好的 JDBC 驱动程序,那么关闭连接应该不会有任何问题。但是有两个问题:
你知道你有没有好的司机吗? 您将来会使用其他 JDBC 驱动程序吗?最好的做法是全部关闭。
【讨论】:
【参考方案6】:在 Java 中,语句(不是结果集)与 Oracle 中的游标相关联。最好关闭您打开的资源,因为 JVM 和系统资源可能会发生意外行为。
此外,一些 JDBC 池框架将语句和连接池化,因此不关闭它们可能不会将这些对象标记为池中的空闲对象,并导致框架出现性能问题。
一般来说,如果一个对象上有一个 close() 或 destroy() 方法,那么调用它是有原因的,如果忽略它,后果自负。
【讨论】:
【参考方案7】:我确实看到了未关闭的 ResultSets 的问题,一直关闭它们有什么坏处,对吧?需要记住这样做的不可靠性是迁移到为您管理这些细节的框架的最佳理由之一。这在您的开发环境中可能不可行,但我很幸运使用 Spring 来管理 JPA 事务。打开连接、语句、结果集以及编写过于复杂的 try/catch/finally 块(在 finally 块中使用 try/catch 块!)以再次关闭它们的混乱细节就消失了,只剩下你实际上完成了一些工作。我强烈建议迁移到这种解决方案。
【讨论】:
它在这个应用程序中的一点。我们使用EJB,这部分是一种插件——我们的客户端会在服务器上部署一个数据源,然后可以通过这个插件查询它。【参考方案8】:在这种情况下,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) 等,而不必担心关闭扫描可能会抛出的异常.
【讨论】:
但我没有看到任何关于关闭连接会关闭它创建的任何资源的信息。 docs.oracle.com/javase/6/docs/api/java/sql/… 释放此连接对象的数据库和JDBC 资源立即但是,关闭所有东西总是更好。以上是关于连接关闭时结果集未关闭?的主要内容,如果未能解决你的问题,请参考以下文章
结果集未打开。不允许操作“getString”。验证自动提交是不是关闭。?