重复使用Statement和Resultset会释放其以前使用的资源吗?还是我必须在重用之前明确关闭它们?

20

示例代码:

        aStmt = aConn.prepareStatement(aQuery);
        aRset = aStmt.executeQuery(cQuery);

        while (cRset.next()) {
            //stuff to determine value of parm1

            aStmt.setString(1, parm1);                
            aRset = aStmt.executeQuery(); 

            //more stuff
        }

在 while 循环语句中的每个循环结束后,我是否需要关闭 aStmt 和 aRset?或者我可以在后续循环中重复使用它们以释放前一次循环使用的内存/资源?

3个回答

27

Java API中明确记录了结果集与(prepared)语句的行为。建议您阅读实际文档(以及JDBC规范)以获取详细信息。

Statement API说:

默认情况下,每个Statement对象只能同时打开一个ResultSet对象。因此,如果对一个ResultSet对象的读取与对另一个ResultSet对象的读取交替进行,则每个对象必须由不同的Statement对象生成。 Statement接口中的所有执行方法都会隐式关闭语句的当前ResultSet对象(如果存在已打开的对象)。

(重点是我的)。

在您特定的代码中,当调用aStmt.executeQuery()时,旧的ResultSet分配给aRset将被驱动程序隐式关闭。尽管如此,最好显式地关闭它自己(或使用Java 7的try-with-resources),以防您在循环的最后一次迭代中忘记关闭ResultSet

现在来看PreparedStatement:当您准备一个语句时(通常情况下,实现可能会有所不同),查询会被发送到服务器进行编译。在执行时,该特定执行的参数被发送到服务器。调用aStmt.close()将导致服务器上的已准备语句被取消分配,这显然不是您想要的,因为您希望重复使用具有不同参数值的语句。

简而言之:

  1. 在此处技术上不必关闭ResultSet(除了最后创建的ResultSet),但最好显式地关闭它
  2. 只有在完成语句时才应关闭PreparedStatement

使用try-with-resources是消除这些问题部分混淆的一种方法,因为在使用范围结束时,您的代码将自动释放资源:

try (
    ResultSet cRset = cStmt.executeQuery(cQuery);
    PreparedStatement aStmt = aConn.prepareStatement(aQuery);
) {
    while (cRset.next()) {
        //stuff to determine value of parm1

        aStmt.setString(1, parm1);                
        try (ResultSet aRset = aStmt.executeQuery()) {
            //more stuff
        }
    }
}
在这段代码的结尾处,所有的JDBC资源都被正确关闭了(按照正确的顺序,即使发生异常等情况)。

实际上,对于最后创建的ResultSet(以及所有其他未关闭的资源),我有一个在程序结束时关闭所有内容的方法。考虑到这一点,我现在的逻辑是否可以保持不变? - heisenbergman
@heisenbergman 当你不再需要资源时,应该立即关闭它们。让它们保持打开状态可能会增加应用程序和数据库的内存使用。 - Mark Rotteveel
更好的做法是在 finally 语句中完成关闭操作,以确保它总是被执行。 - Mike Lowery
1
@MikeLowery 在我的答案中使用 try-with-resources 是比使用 finally 更好的选择。 - Mark Rotteveel

2

不,你不能在while循环内关闭ResultSetStatement

你必须在循环后关闭它们。

如果想要重复使用PreparedStatement,则只有在完成处理后才能关闭它。

最好的规则是在创建它们的同一块中关闭这些资源。在你的情况下,最好的做法是在捕获SQLException后的finally块中关闭这些资源。

例如:

try {
    aStmt = aConn.prepareStatement(aQuery);
    cRset = cStmt.executeQuery(cQuery);

    while (cRset.next()) {
        //stuff to determine value of parm1

        aStmt.setString(1, parm1);
        try {
            aRset = aStmt.executeQuery();
        } finally {
            aRset.close();
        }

        //more stuff
    }
} catch (SQLException ex) {
    // Do error handling
} finally {
    // Close Resultset
}

在Java 7中,您可以使用try-with-resources语句。

我不喜欢的是在 ResultSet 上调用 close() 可能会导致 SQLException,需要处理,使代码变得更加混乱。 - Mike Lowery

0
PreparedStatement API: SQL语句经预编译并存储于PreparedStatement对象。该对象可以被用于高效地执行多次该语句。
但是,ResultSet对象不能被重复使用。当你第二次在PreparedStatement对象上调用executeQuery时,如果你没有关闭前一个ResultSet,就有可能造成资源泄漏。

2
JDBC规范要求如果从同一个Statement对象创建了新的ResultSet,则必须关闭旧的ResultSet - Mark Rotteveel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接