重用语句和结果集是不是会释放其先前使用的资源?还是我必须在重用之前明确关闭它们?

Posted

技术标签:

【中文标题】重用语句和结果集是不是会释放其先前使用的资源?还是我必须在重用之前明确关闭它们?【英文标题】:Does reusing a Statement and Resultset release resources from its previous usage? Or do I have to explicitly close them before reuse?重用语句和结果集是否会释放其先前使用的资源?还是我必须在重用之前明确关闭它们? 【发布时间】:2013-07-10 16:03:40 【问题描述】:

示例代码:

        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?或者在后续循环中重用它们会释放之前循环使用的内存/资源?

【问题讨论】:

【参考方案1】:

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

Statement API 说:

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

(强调我的)。

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

现在到PreparedStatement:当你准备一个语句时(通常,实现可能会有所不同),查询被发送到服务器进行编译。在执行时,该特定执行的参数被发送到服务器。在aStmt 上调用close() 将导致准备好的语句在服务器上被释放,这显然是不是你想要的,因为你想重新使用具有不同参数值的语句.

总之

    在这里关闭ResultSet 在技术上不是必需的(除了最后创建的ResultSet),但最好明确地这样做 您只应在完成后关闭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 您应该在不再需要资源时立即关闭它们。让它们保持打开状态可能会增加应用程序和数据库中的内存使用量。【参考方案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。

【讨论】:

【参考方案3】:

PreparedStatement API:SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用该对象多次有效地执行该语句。

但是您不能重用 ResultSet 对象。当您在第二次创建新的 ResultSet 时对 PreparedStatement 对象调用 executeQuery 时,如果您不关闭之前的 ResultSet,您将面临资源泄漏的风险。

【讨论】:

JDBC 规范要求驱动程序关闭旧的ResultSet,如果从同一个Statement 对象创建新的@。

以上是关于重用语句和结果集是不是会释放其先前使用的资源?还是我必须在重用之前明确关闭它们?的主要内容,如果未能解决你的问题,请参考以下文章

异常处理(下)

数据库查询优化技术

《Java从小白到大牛》之第14章 异常处理(下)

Beam Search生成的句子基本都一样,是不是有方法扩展生成句子的多样性?

db2数据库中delete表中的数据,reorg表之后,表空间并没有被释放,坐等结果

With语句以及@contextmanager的语法解析