You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JDBC使用两个ResultSet时报Operation not allowed after ResultSet closed异常

问题排查结果

核心错误原因

你遇到的Operation not allowed after ResultSet closed异常确实和你同时使用两个ResultSet绑定同一个Statement有关,JDBC规范明确要求:一个Statement实例同时只能关联一个打开的ResultSet,当你调用statement_object.executeQuery(sql_query_str2)生成第二个ResultSet时,第一个result_set会被自动关闭,后续循环调用result_set.next()就会触发该异常。

代码中存在的所有问题列表

  • 同一个Statement实例绑定多个ResultSet,导致第一个ResultSet被自动关闭
  • 第二个ResultSet result_set2没有调用next()移动指针到有效行,直接调用getInt("movie_id")会抛出非法操作异常
  • 插入SQL仅定义了1个参数占位符,id2==id分支调用setInt(2, nativeName_length)使用了不存在的第2个占位符,会抛出参数索引越界异常
  • 业务逻辑错误:你预期是匹配两张表的movie_id对应写入长度,但当前全表查询后未遍历result_set2,永远只能拿到result_set2的第一行数据,无法实现id匹配逻辑
  • 资源未释放:Connection、Statement、ResultSet、PreparedStatement使用完成后未调用close()方法关闭,会造成数据库连接资源泄露
  • 循环内重复创建PreparedStatement,性能低下且浪费资源

修复方案

建议每个ResultSet绑定独立的Statement实例,同时修正插入逻辑和资源释放逻辑,修正后参考代码如下:

public static void updateLength() {
    // 定义资源变量,finally里统一关闭
    Connection db_connection = null;
    Statement statement_movie = null;
    Statement statement_number = null;
    ResultSet result_set = null;
    ResultSet result_set2 = null;
    PreparedStatement preparedStmt = null;
    try {
        db_connection = DriverManager.getConnection("jdbc:mysql://localhost/omdb", "root", "");
        // 每个查询用独立的Statement
        statement_movie = db_connection.createStatement();
        statement_number = db_connection.createStatement();

        String sql_query_str = "SELECT * FROM movies ";
        String sql_query_str2 = "SELECT * FROM movie_numbers ";

        result_set = statement_movie.executeQuery(sql_query_str);
        result_set2 = statement_number.executeQuery(sql_query_str2);
        
        // 先把movie_numbers的id存到集合里做匹配,避免重复遍历
        Set<Integer> existIds = new HashSet<>();
        while (result_set2.next()) {
            existIds.add(result_set2.getInt("movie_id"));
        }
        
        // 预编译SQL语句复用提升性能
        String insertSql = "insert into movie_numbers (movie_id, length) values (?, ?)";
        String updateSql = "update movie_numbers set length = ? where movie_id = ?";
        while (result_set.next()) {
            String native_name = result_set.getString("native_name");
            int nativeName_length = API.getLength(native_name);
            int id = result_set.getInt("movie_id");
            
            if (existIds.contains(id)) {
                // 已存在对应movie_id执行更新
                preparedStmt = db_connection.prepareStatement(updateSql);
                preparedStmt.setInt(1, nativeName_length);
                preparedStmt.setInt(2, id);
            } else {
                // 不存在对应movie_id执行新增
                preparedStmt = db_connection.prepareStatement(insertSql);
                preparedStmt.setInt(1, id);
                preparedStmt.setInt(2, nativeName_length);
            }
            preparedStmt.executeUpdate();
            preparedStmt.close();
        }
        
    } catch (Exception e) {
        System.err.println("Got an exception!");
        System.err.println(e.getMessage());
        e.printStackTrace();
    } finally {
        // 逆序关闭所有资源避免泄露
        try { if (result_set != null) result_set.close(); } catch (Exception e) {}
        try { if (result_set2 != null) result_set2.close(); } catch (Exception e) {}
        try { if (statement_movie != null) statement_movie.close(); } catch (Exception e) {}
        try { if (statement_number != null) statement_number.close(); } catch (Exception e) {}
        try { if (preparedStmt != null) preparedStmt.close(); } catch (Exception e) {}
        try { if (db_connection != null) db_connection.close(); } catch (Exception e) {}
    }
}

更优优化建议

无需两次全表查询,给movie_numbers表的movie_id字段设置唯一键后,直接用MySQL特有的INSERT ... ON DUPLICATE KEY UPDATE语法即可一次操作完成写入/更新逻辑,性能提升明显。

内容的提问来源于stack exchange,提问作者Nathan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 21:15:02