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
相关产品推荐
相关产品推荐

