SpringBoot3.2中Hibernate ScrollableResult无法正常工作求助
核心问题原因
Spring Boot 3.2默认依赖的Hibernate 6.4对ScrollableResults的执行逻辑做了调整:采用**延迟执行(DeferredResultSetAccess)**机制——调用scroll()时不会立即执行SQL获取结果集,而是等到首次调用result.next()时才真正触发查询。如果此时创建Session的上下文已结束(比如事务已提交/回滚、EntityManager被容器回收),对应的JDBC连接会被提前关闭,从而抛出该异常。
而Spring Boot 3.1依赖的Hibernate 6.2中,scroll()会立即执行查询并持有结果集,因此不会出现这个问题。
可行解决方案
方案1:确保遍历过程处于活跃的Session/事务上下文内
必须保证从创建ScrollableResults到遍历结束的全流程,都处于活跃的事务或Session生命周期中。
修改代码示例:
// 用@Transactional注解确保整个流程在事务内执行 @Transactional public void processScrollableData() { Session session = entityManager.unwrap(Session.class); org.hibernate.query.Query query = buildParameterizedQuery(); ScrollableResults result = query.setReadOnly(true) .setCacheable(false) .scroll(ScrollMode.FORWARD_ONLY); try { while (result.next()) { // 处理单条数据 Object rowData = result.get(); // ...业务逻辑 } } finally { // 显式关闭ScrollableResults释放资源 result.close(); } }
注意:如果是手动管理Session,必须等遍历完成后再关闭Session,不能提前释放。
方案2:强制立即执行查询,规避延迟加载
通过设置特定参数,让Hibernate在调用scroll()时就立即执行SQL并获取结果集,避免延迟执行导致的连接关闭问题。
可以给查询添加setFetchSize(Integer.MIN_VALUE):
ScrollableResults result = query.setReadOnly(true) .setCacheable(false) .setFetchSize(Integer.MIN_VALUE) // 强制立即执行查询 .scroll(ScrollMode.FORWARD_ONLY);
该参数会触发JDBC驱动使用流式结果集,同时强制Hibernate立即执行SQL,确保结果集在Session活跃时就被获取。
方案3:用Hibernate Stream替代ScrollableResults
Hibernate 6+官方更推荐使用Stream处理大数据量遍历,它的资源管理逻辑更适配Spring事务上下文:
@Transactional public void processDataWithStream() { Session session = entityManager.unwrap(Session.class); // 用try-with-resources自动管理Stream资源 try (Stream<Object> dataStream = buildParameterizedQuery().stream()) { dataStream.forEach(row -> { // 处理单条数据 // ...业务逻辑 }); } }
Stream会在事务结束时自动关闭资源,既避免连接泄漏,也适配Hibernate 6.4的新执行逻辑。
为什么之前的尝试未生效?
- 如果添加的事务仅覆盖了
ScrollableResults的创建过程,遍历操作在事务外执行,依然会出现连接关闭问题。 - 锁模式设置不影响JDBC连接的生命周期管理,因此无法解决该异常。
内容的提问来源于stack exchange,提问作者Antani

