关闭Statement时MySQL随机抛出SQLException问题求助
问题分析与解决
代码场景
现有自动生成的代码如下:
Statement statement = createStatement(); try (java.sql.ResultSet resultset = statement.executeQuery()) { while (resultset.next()) { myObject.setProcessingDate(resultset.getDate(1)); myObject.set... } } finally { if (statement != null) { statement.close(); } }
已知Statement应当放入try-with-resources块管理,但受限于自动生成逻辑无法修改,目前会随机抛出以下异常:
异常信息
caused by: java.sql.SQLException: Streaming result set com.mysql.cj.protocol.a.result.ResultsetRowsStreaming@9h6d41205 is still active. No statements may be issued when any streaming result sets are open and in use on a given connection. Ensure that you have called .close() on any active streaming result sets before attempting more queries. at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) at com.mysql.cj.jdbc.ServerPreparedStatement.realClose(ServerPreparedStatement.java:549) at com.mysql.cj.jdbc.ServerPreparedStatement.close(ServerPreparedStatement.java:284) at com.zaxxer.hikari.pool.ProxyStatement.close(ProxyStatement.java:76) at mypackage.NamedParameterStatement.close(NamedParameterStatement.java:520)
问题原因
你忽略了MySQL流式结果集的特殊行为:
- 普通结果集会一次性加载所有数据到客户端内存,此时调用
Statement.close()会自动关闭关联的ResultSet;但流式结果集是逐行从服务器拉取数据,服务器端会保持结果集的活跃状态,直到客户端遍历完所有行或主动关闭ResultSet。 - try-with-resources确实会在try块结束后自动关闭
ResultSet,但如果循环过程中因异常(如网络波动、数据解析错误)提前退出,ResultSet可能未完成服务器端的资源释放流程,处于"半活跃"状态。 - MySQL驱动在流式结果集未完全关闭时,调用
Statement.close()不会自动清理ResultSet,反而会抛出该异常——这是驱动的保护机制,避免服务器端资源泄漏。 - 异常随机出现的原因:当循环正常遍历完所有行时,
ResultSet会自动完成关闭流程,此时Statement.close()可正常执行;仅当循环提前中断时,才会触发该错误。
解决办法
- 最优方案:修改自动生成逻辑,将
Statement放入try-with-resources
try-with-resources会按逆序自动关闭资源,先关闭ResultSet再关闭Statement,完全匹配流式结果集的要求:try (Statement statement = createStatement(); ResultSet resultset = statement.executeQuery()) { while (resultset.next()) { myObject.setProcessingDate(resultset.getDate(1)); // 其他属性设置 } } - 临时兼容方案(无法修改自动生成代码时)
在finally块关闭Statement前,手动确保ResultSet已关闭(即使try-with-resources已处理,额外增加一次检查):
或者在循环中断时,主动遍历完剩余行(如果业务允许),确保服务器端结果集被正常关闭。Statement statement = createStatement(); ResultSet resultset = null; try { resultset = statement.executeQuery(); while (resultset.next()) { myObject.setProcessingDate(resultset.getDate(1)); myObject.set... } } finally { if (resultset != null) { try { resultset.close(); } catch (SQLException e) { // 记录日志,不影响后续流程 } } if (statement != null) { statement.close(); } }
内容的提问来源于stack exchange,提问作者Zizou
相关产品推荐
相关产品推荐

