ResultSet已关闭报错排查:ERRORCODE=-4470,SQLSTATE=null
问题排查:ResultSet已关闭报错(ERRORCODE=-4470)
现有ID列表:'01/A', '02/B', '03/C', '04/D', '05/E',需从conB库的MASTER表中读取存在的ID对应的STATUS字段,插入到conA库的TEMP表,跳过不存在的ID。执行代码时,在rs.next()处触发报错:
Invalid operation: result set is closed. ERRORCODE=-4470, SQLSTATE=null
关联代码如下:
public int insertData(List<String> idList, Connection conA, Connection conB) throws SQLException, SQLTransientConnectionException, Exception { int insertStatus = 0; String id = null; for (int i = 0; i < idList.size(); i++) { String sql = "SELECT * FROM MASTER WHERE ID LIKE '%" + idList.get(i) + "%')"; PreparedStatement ps = conB.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while (rs.next()) { // 报错位置 try { id = rs.getString("ID"); status = rs.getString("STATUS"); String sql1 = "INSERT INTO TEMP (ID, STATUS) VALUES ('" + id + "','" + status + "')"; PreparedStatement ps1 = conA.prepareStatement(sql1); insertStatus = ps1.executeUpdate(); ps1.close(); log.info("Number of rows inserted " + insertStatus); } catch (SQLException e) { e.printStackTrace(); } } ps.close(); rs.close(); } return insertStatus; }
问题分析与修复方案
1. 触发ResultSet关闭的直接原因
循环内遍历conB的ResultSet时,同时使用conA创建并执行新的Statement。部分JDBC驱动(如DB2,ERRORCODE=-4470是其典型错误)默认不支持同一事务内跨Connection的Statement并行操作,执行conA的插入时会间接强制关闭conB的ResultSet。
2. 其他致命问题
- SQL语法错误:SELECT语句末尾多了一个右括号
),直接导致SQL执行失败,ResultSet初始化后即处于关闭状态。 - SQL注入风险:直接拼接字符串生成SQL,存在严重注入漏洞。
- 资源泄漏:手动关闭资源的逻辑存在异常时的泄漏风险,且关闭顺序不合理(应先关ResultSet再关Statement)。
- 变量未定义:
status变量未声明,会导致编译错误。 - 结果统计错误:
insertStatus仅保留最后一次插入的行数,无法统计总插入量。
修复后的代码
public int insertData(List<String> idList, Connection conA, Connection conB) throws SQLException, SQLTransientConnectionException, Exception { int totalInserted = 0; // 预编译查询与插入SQL,复用提升效率 String querySql = "SELECT ID, STATUS FROM MASTER WHERE ID LIKE ?"; String insertSql = "INSERT INTO TEMP (ID, STATUS) VALUES (?, ?)"; // try-with-resources自动管理资源,无需手动close try (PreparedStatement queryPs = conB.prepareStatement(querySql); PreparedStatement insertPs = conA.prepareStatement(insertSql)) { for (String targetId : idList) { // 绑定查询参数,处理LIKE通配符 queryPs.setString(1, "%" + targetId + "%"); try (ResultSet rs = queryPs.executeQuery()) { while (rs.next()) { String dbId = rs.getString("ID"); String status = rs.getString("STATUS"); // 绑定插入参数,避免SQL注入 insertPs.setString(1, dbId); insertPs.setString(2, status); int rows = insertPs.executeUpdate(); totalInserted += rows; log.info("本次插入行数: " + rows); } } } } catch (SQLException e) { log.error("数据插入失败", e); throw e; } return totalInserted; }
修复说明
- 用try-with-resources自动管理Statement和ResultSet,彻底解决资源泄漏与关闭顺序问题。
- 预编译SQL并使用参数绑定,消除SQL注入风险,同时减少SQL编译次数提升性能。
- 修正SELECT语句的语法错误,移除多余的右括号。
- 用
totalInserted累计总插入行数,符合需求逻辑。 - 将查询和插入的PreparedStatement提前初始化,避免循环内重复创建销毁,优化性能。
内容的提问来源于stack exchange,提问作者Mohan
相关产品推荐
相关产品推荐

