返回ResultSet后触发‘结果集已关闭’异常,如何兼顾取数与关闭连接?
解决ResultSet被提前关闭的问题
问题根源
你遇到的问题确实是因为try-with-resources会在try块执行完毕后自动关闭CallableStatement和Connection,而ResultSet的生命周期依赖于创建它的Statement,一旦Statement关闭,ResultSet也会被强制关闭,导致后续读取数据时抛出异常。
最小改动解决方案
核心思路是不要直接返回ResultSet,而是在方法内部将ResultSet中的数据读取到内存集合中,再返回集合。这样数据库资源可以在方法内安全关闭,调用方操作的是内存中的数据,不受数据库资源生命周期影响。
方案1:返回实体类集合(推荐,类型安全)
假设你有对应US州信息的实体类USState,修改方法如下:
import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public List<USState> getUSStates() throws SQLException { List<USState> states = new ArrayList<>(); try( Connection inGlobalConnections = getConnection(); CallableStatement csmt = inGlobalConnections.prepareCall("{call mst.USP_GetUSStates()}"); ResultSet rs = csmt.executeQuery()) { // 将ResultSet也纳入try-with-resources自动管理 while (rs.next()) { USState state = new USState(); state.setStateId(rs.getInt("StateId")); // 替换为实际字段名 state.setStateName(rs.getString("StateName")); // 替换为实际字段名 // 根据实际表结构添加其他字段的赋值逻辑 states.add(state); } } catch (Exception e) { _log.error(e, e); throw e; // 重新抛出异常,让调用方感知错误 } return states; }
方案2:返回Map集合(无需新建实体类,改动极小)
如果不想创建实体类,可以用List<Map<String, Object>>来存储每行数据,适用于快速改造多个类似方法:
import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public List<Map<String, Object>> getUSStates() throws SQLException { List<Map<String, Object>> states = new ArrayList<>(); try( Connection inGlobalConnections = getConnection(); CallableStatement csmt = inGlobalConnections.prepareCall("{call mst.USP_GetUSStates()}"); ResultSet rs = csmt.executeQuery()) { ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); while (rs.next()) { Map<String, Object> rowMap = new HashMap<>(); for (int i = 1; i <= columnCount; i++) { rowMap.put(metaData.getColumnName(i), rs.getObject(i)); } states.add(rowMap); } } catch (Exception e) { _log.error(e, e); throw e; } return states; }
改动说明
- 两种方案都只需修改方法的返回类型,以及在try块内添加数据读取逻辑,其他代码结构基本不变,符合你"最小改动"的需求。
- 所有数据库资源(Connection、CallableStatement、ResultSet)都通过
try-with-resources自动关闭,无需手动管理,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Anubhav kalra
相关产品推荐
相关产品推荐

