如何避免 ResultSet 是 Java 中的关闭异常?

Posted

技术标签:

【中文标题】如何避免 ResultSet 是 Java 中的关闭异常?【英文标题】:How can I avoid ResultSet is closed exception in Java? 【发布时间】:2010-10-30 10:45:14 【问题描述】:

一旦我的代码进入我的while(rs.next()) 循环,它就会产生ResultSet is closed 异常。是什么导致了这个异常,我该如何纠正它?

编辑:我注意到我在我的代码中嵌套了 while(rs.next()) 循环和另一个 (rs2.next()),两个结果集都来自同一个数据库,这是一个问题吗?

【问题讨论】:

【参考方案1】:

听起来您在遍历第一条语句的结果集之前在同一连接中执行了另一条语句。如果您嵌套处理来自同一个数据库的两个结果集,那么您做错了。这些集合的组合应该在数据库端完成。

【讨论】:

并非所有驱动程序和 RDBMS 都如此。 sqlserver.jar 不能嵌套,但是 jtds.jar 可以。【参考方案2】:

这可能是由多种原因造成的,包括您使用的驱动程序。

a) 一些驱动程序不允许嵌套语句。根据您的驱动程序是否支持 JDBC 3.0,您应该在创建 Statement 对象时检查第三个参数。例如,我在使用 JayBird 驱动程序到 Firebird 时遇到了同样的问题,但代码在 postgres 驱动程序中运行良好。然后我将第三个参数添加到 createStatement 方法调用并将其设置为 ResultSet.HOLD_CURSORS_OVER_COMMIT,并且代码也开始在 Firebird 上正常工作。

static void testNestedRS() throws SQLException 

    Connection con =null;
    try 
        // GET A CONNECTION
        con = ConexionDesdeArchivo.obtenerConexion("examen-dest");
        String sql1 = "select * from reportes_clasificacion";

        Statement st1 = con.createStatement(
                ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_READ_ONLY, 
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        ResultSet rs1 = null;

        try 
            // EXECUTE THE FIRST QRY
            rs1 = st1.executeQuery(sql1);

            while (rs1.next()) 
                // THIS LINE WILL BE PRINTED JUST ONCE ON
                                    // SOME DRIVERS UNLESS YOU CREATE THE STATEMENT 
                // WITH 3 PARAMETERS USING 
                                    // ResultSet.HOLD_CURSORS_OVER_COMMIT
                System.out.println("ST1 Row #: " + rs1.getRow());

                String sql2 = "select * from reportes";
                Statement st2 = con.createStatement(
                        ResultSet.TYPE_SCROLL_INSENSITIVE,
                        ResultSet.CONCUR_READ_ONLY);

                // EXECUTE THE SECOND QRY.  THIS CLOSES THE FIRST 
                // ResultSet ON SOME DRIVERS WITHOUT USING 
                                    // ResultSet.HOLD_CURSORS_OVER_COMMIT

                st2.executeQuery(sql2);

                st2.close();
            
         catch (SQLException e) 
            e.printStackTrace();
         finally 
            rs1.close();
            st1.close();
        

     catch (SQLException e) 

     finally 
        con.close();

    


b) 您的代码中可能存在错误。请记住,您不能重用 Statement 对象,一旦您对同一个语句对象重新执行查询,与该语句关联的所有打开的结果集都将关闭。确保您没有关闭该语句。

【讨论】:

我在使用 Firebird JDBC 驱动程序时遇到了同样的问题。我可以验证ResultSet.HOLD_CURSORS_OVER_COMMIT 是否正常工作。 请注意,使用 Jaybird(以及一般的 JDBC)解决此问题的正确方法是在执行多个语句之前禁用自动提交。【参考方案3】:

此外,您只能从每个语句中打开一个结果集。因此,如果您同时遍历两个结果集,请确保它们在不同的语句上执行。在一个语句上打开第二个结果集将隐式关闭第一个。 http://java.sun.com/javase/6/docs/api/java/sql/Statement.html

【讨论】:

【参考方案4】:

异常表明您的结果已关闭。您应该检查您的代码并查找您发出ResultSet.close() 调用的所有位置。还要寻找Statement.close()Connection.close()。当然,其中一个会在调用 rs.next() 之前被调用。

【讨论】:

【参考方案5】:

您可能已经关闭了产生 ResultSetConnectionStatement,这将导致 ResultSet 也被关闭。

【讨论】:

【参考方案6】:

正确的 jdbc 调用应该类似于:

try  
    Connection conn;
    Statement stmt;
    ResultSet rs; 

    try 
        conn = DriverManager.getConnection(myUrl,"",""); 
        stmt = conn.createStatement(); 
        rs = stmt.executeQuery(myQuery); 

        while ( rs.next() )  
            // process results
         

     catch (SqlException e)  
        System.err.println("Got an exception! "); 
        System.err.println(e.getMessage()); 
     finally 
        // you should release your resources here
        if (rs != null)  
            rs.close();
        

        if (stmt != null) 
            stmt.close();
        

        if (conn != null) 
            conn.close();
        
    
 catch (SqlException e) 
    System.err.println("Got an exception! "); 
    System.err.println(e.getMessage()); 

只有在从结果集中获得结果后才能关闭连接(或语句)。最安全的方法是在finally 块中进行。但是close() 也可能会影响SqlException,因此是另一个try-catch 块。

【讨论】:

一定会喜欢嵌套的 try/catch。有时我真的很讨厌 JDBC。它确实使这个例子膨胀,但你真的应该关闭结果集和语句。您确实称其为正确的 JDBC 调用!【参考方案7】:

我得到了同样的错误,一切都是正确的,只是我使用相同的语句接口对象来执行和更新数据库。 分离后,即使用语句接口的不同对象来更新和执行查询后,我解决了这个错误。即,不要使用相同的语句对象来更新和执行查询。

【讨论】:

【参考方案8】:

检查您是否已将执行此代码的方法声明为static。如果是static,则可能有其他线程正在重置ResultSet

【讨论】:

【参考方案9】:

ResultSetClosedException 被抛出有两个原因。

1.) 您打开了另一个与数据库的连接,但没有关闭所有其他连接。

2.) 您的 ResultSet 可能没有返回任何值。所以当你尝试从 ResultSet 访问数据时,java 会抛出一个ResultSetClosedException

【讨论】:

我在没有关闭第一个的情况下建立了两个连接。这会导致“结果集已关闭”错误。感谢您在这里强调原因。【参考方案10】:

确保在运行 rs.next 之前已关闭所有状态和结果集。 最后保证了这一点

public boolean flowExists( Integer idStatusPrevious, Integer idStatus, Connection connection ) 
    LogUtil.logRequestMethod();

    PreparedStatement ps = null;
    ResultSet rs = null;
    try 
        ps = connection.prepareStatement( Constants.SCRIPT_SELECT_FIND_FLOW_STATUS_BY_STATUS );
        ps.setInt( 1, idStatusPrevious );
        ps.setInt( 2, idStatus );

        rs = ps.executeQuery();

        Long count = 0L;

        if ( rs != null ) 
            while ( rs.next() ) 
                count = rs.getLong( 1 );
                break;
            
        

        LogUtil.logSuccessMethod();

        return count > 0L;
     catch ( Exception e ) 
        String errorMsg = String
            .format( Constants.ERROR_FINALIZED_METHOD, ( e.getMessage() != null ? e.getMessage() : "" ) );
        LogUtil.logError( errorMsg, e );

        throw new FatalException( errorMsg );
     finally 
        rs.close();
        ps.close();
    

【讨论】:

【参考方案11】:

在使用 ResultSet 而不是在 @Transactional 方法中时也会发生这种情况。

ScrollableResults results = getScrollableResults("select e from MyEntity e");
while (results.next()) 
   ...

results.close();

如果 MyEntity 与其他实体有热切的关系。第二次调用 results.next() 会引发 ResultSet is closed 异常。

因此,如果您在具有急切关系的实体上使用 ScrollableResults,请确保您的方法以事务方式运行。

【讨论】:

以上是关于如何避免 ResultSet 是 Java 中的关闭异常?的主要内容,如果未能解决你的问题,请参考以下文章

如何在以下几种方法中关闭 resultSet、prepareStatement、conn 以避免 rs 关闭和连接池被卡住

如何从 JAVA 中的 ResultSet 或 ResultSetMetaData 对象中获取数据库表的主键的列名?

ResultSet 关闭后不允许出现 ResultSet、空指针异常和 SQLException 操作

如何在java中获取Resultset的长度或大小[重复]

java中如何获取ResultSet rs结果集中的条数?

Java中,将ResultSet映射为对象和队列及其他辅助函数