JDBC结果集和语句必须分开关闭,尽管连接随后关闭?

据说在使用完JDBC资源后关闭所有资源是一个好习惯。但是如果我有下面的代码,是否有必要关闭Resultset和Statement?

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = // Retrieve connection
stmt = conn.prepareStatement(// Some SQL);
rs = stmt.executeQuery();
} catch(Exception e) {
// Error Handling
} finally {
try { if (rs != null) rs.close(); } catch (Exception e) {};
try { if (stmt != null) stmt.close(); } catch (Exception e) {};
try { if (conn != null) conn.close(); } catch (Exception e) {};
}

问题是关闭连接是否有效,或者是否会留下一些可用的资源。

234658 次浏览

我现在使用Oracle和Java。以下是我的观点:

你应该显式地关闭ResultSetStatement,因为Oracle以前在关闭连接后仍然保持游标打开的问题。如果不关闭ResultSet(游标),它将抛出类似超过最大打开游标的错误。

我认为您在使用其他数据库时可能会遇到同样的问题。

下面是教程完成时关闭ResultSet:

完成时关闭ResultSet

完成后立即关闭ResultSet对象 甚至使用ResultSet对象 Statement对象关闭 ResultSet对象 close,显式关闭ResultSet 给垃圾收集器机会 尽早回忆 因为ResultSet对象可能占用 大量内存取决于查询

ResultSet.close();

javadocs:

Statement对象关闭时,它的 current ResultSet对象,如果有

然而,当你关闭底层的Connection时,javadocs并不清楚StatementResultSet是否被关闭。它们简单地声明关闭一个连接:

释放Connection对象的 数据库和JDBC资源 立即而不是等待

.

.

在我看来,当你用完ResultSetsStatementsConnections时,总是显式地关闭它们,因为不同的数据库驱动程序对close的实现可能不同。

通过使用Apache中的DBUtils中的closeQuietly等方法,可以为自己节省大量的样板代码。

你所做的是完美的,非常好的练习。

我之所以说这是一种很好的实践……例如,如果由于某种原因,你正在使用一个“原始”类型的数据库池,并且你调用了connection.close(),连接将返回到池中,并且ResultSet/Statement将永远不会关闭,然后你将遇到许多不同的新问题!

所以你不能总是指望connection.close()来清理。

我希望这对你有所帮助:)

关闭与JDBC this相关的资源的正确且安全的方法(取自如何正确地关闭JDBC资源-每次):

Connection connection = dataSource.getConnection();
try {
Statement statement = connection.createStatement();


try {
ResultSet resultSet = statement.executeQuery("some query");


try {
// Do stuff with the result set.
} finally {
resultSet.close();
}
} finally {
statement.close();
}
} finally {
connection.close();
}

由于try-with-resources声明. Java 1.7使我们的生活更容易。

try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
try (ResultSet resultSet = statement.executeQuery("some query")) {
// Do stuff with the result set.
}
try (ResultSet resultSet = statement.executeQuery("some query")) {
// Do more stuff with the second result set.
}
}

这个语法非常简洁和优雅。并且即使无法创建statementconnection也将被关闭。

使用Java 6表单,我认为最好在关闭之前检查它是否关闭(例如,如果一些连接池在其他线程中驱逐了连接)-例如一些网络问题-语句和结果集状态可以关闭。(这是不经常发生的,但我在Oracle和DBCP有这个问题)。我的模式是(在旧的Java语法中):

try {
//...
return resp;
} finally {
if (rs != null && !rs.isClosed()) {
try {
rs.close();
} catch (Exception e2) {
log.warn("Cannot close resultset: " + e2.getMessage());
}
}
if (stmt != null && !stmt.isClosed()) {
try {
stmt.close();
} catch (Exception e2) {
log.warn("Cannot close statement " + e2.getMessage());
}
}
if (con != null && !conn.isClosed()) {
try {
con.close();
} catch (Exception e2) {
log.warn("Cannot close connection: " + e2.getMessage());
}
}
}

理论上,它不是100%完美的,因为在检查关闭状态和关闭本身之间,有一点空间来改变状态。在最坏的情况下,你很快就会得到警告。-但在长期运行的查询中,它小于状态变化的可能性。我们在“平均”负载(150个同时用户)的生产环境中使用了这个模式,我们没有遇到任何问题——所以从来没有看到警告消息。

如果你想要更紧凑的代码,我建议使用Apache Commons DbUtils。在这种情况下:

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = // Retrieve connection
stmt = conn.prepareStatement(// Some SQL);
rs = stmt.executeQuery();
} catch(Exception e) {
// Error Handling
} finally {
DbUtils.closeQuietly(rs);
DbUtils.closeQuietly(stmt);
DbUtils.closeQuietly(conn);
}

不,您不需要关闭连接以外的任何东西。根据JDBC规范,关闭任何较高的对象都会自动关闭较低的对象。关闭Connection将关闭该连接创建的所有__abc1。关闭任何Statement将关闭由该Statement创建的所有__abc3。Connection是否可池并不重要。即使是可入池的连接也必须在返回池之前进行清洁。

当然,你可能在Connection上有很长的嵌套循环,创建了很多语句,然后关闭它们是合适的。我几乎从不关闭ResultSet,但关闭StatementConnection将关闭它们时似乎过度了。

据我所知,在当前的JDBC中,resultset和语句实现了AutoCloseable接口。这意味着它们会在被摧毁或超出范围时自动关闭。

Connection是否可池并不重要。即使是可入池的连接也必须在返回池之前进行清洁。

“干净”通常意味着关闭结果集。回滚任何挂起的事务,但不关闭连接。否则,池化就失去了意义。

一些方便功能:

public static void silentCloseResultSets(Statement st) {
try {
while (!(!st.getMoreResults() && (st.getUpdateCount() == -1))) {}
} catch (SQLException ignore) {}
}
public static void silentCloseResultSets(Statement ...statements) {
for (Statement st: statements) silentCloseResultSets(st);
}

我创建了以下方法来创建可重用的One Liner:

public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
if (!resultSet.isClosed()) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
if (!statement.isClosed()) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}


if (connection != null) {
try {
if (!connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

我在父类中使用此代码,它继承给所有发送DB查询的类。我可以在所有查询上使用联机程序,即使我没有resultSet。方法负责按照正确的顺序关闭ResultSet、Statement、Connection。这是我的最终块的样子。

finally {
oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}