StreamingResponseBody异步场景如何确保OpenEntityManagerInViewFilter关闭EntityManager
异步请求(如StreamingResponseBody)中正确关闭EntityManager的方案
问题根源
同步请求中OpenEntityManagerInViewFilter会在请求结束时统一关闭EntityManager,但StreamingResponseBody这类异步场景下,默认的AsyncRequestInterceptor仅在超时或发生错误时才关闭会话,导致连接长期被占用,引发HikariCP连接泄漏。你之前尝试手动关闭注入的EntityManager无效,是因为@PersistenceContext注入的是请求级实例,其生命周期由Spring容器管控,手动close无法绕过容器的上下文绑定。
可行解决方案
1. 手动创建独立EntityManager并管理生命周期
避免复用请求级的EntityManager,直接从EntityManagerFactory创建独立实例,用完后显式关闭:
@RestController public class MyController { @Autowired private EntityManagerFactory entityManagerFactory; @GetMapping(path = "/stream", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public StreamingResponseBody stream() { return outputStream -> { EntityManager em = null; try { em = entityManagerFactory.createEntityManager(); // 配置批量查询减少内存占用 TypedQuery<MyEntity> query = em.createQuery("SELECT e FROM MyEntity e", MyEntity.class); query.setHint(QueryHints.HINT_FETCH_SIZE, 50); // 流式处理查询结果 Stream<MyEntity> resultStream = query.getResultStream(); resultStream.forEach(entity -> { // 写入输出流的业务逻辑 // 例如:outputStream.write(entity.toString().getBytes()); }); } finally { if (em != null && em.isOpen()) { em.close(); } } }; } }
2. 自定义AsyncRequestInterceptor确保无条件关闭
替换默认的拦截器,让其在异步请求完成(无论成功/失败)时都关闭EntityManager:
第一步:实现自定义拦截器
public class CustomAsyncRequestInterceptor extends AsyncRequestInterceptor { private static final Logger logger = LoggerFactory.getLogger(CustomAsyncRequestInterceptor.class); @Override protected void closeSession() { if (this.sessionHolder != null && this.sessionHolder.getSession() != null) { logger.debug("Closing Hibernate Session after async request completion"); SessionFactoryUtils.closeSession(this.sessionHolder.getSession()); } } @Override public void afterCompletion(NativeWebRequest request, Exception ex) throws Exception { // 无论是否异常,强制关闭会话 closeSession(); super.afterCompletion(request, ex); } }
第二步:配置过滤器使用自定义拦截器
@Configuration public class WebConfig { @Bean public FilterRegistrationBean<OpenEntityManagerInViewFilter> openEntityManagerInViewFilter() { FilterRegistrationBean<OpenEntityManagerInViewFilter> registrationBean = new FilterRegistrationBean<>(); OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter(); filter.setAsyncRequestInterceptor(new CustomAsyncRequestInterceptor()); registrationBean.setFilter(filter); // 设置过滤器优先级,确保在其他过滤器之前执行 registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return registrationBean; } }
3. 使用独立事务管理EntityManager生命周期
通过@Transactional(propagation = Propagation.REQUIRES_NEW)创建独立事务,Spring会在事务结束后自动关闭对应的EntityManager:
@RestController public class MyController { @Autowired private DataStreamingService streamingService; @GetMapping(path = "/stream", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public StreamingResponseBody stream() { return outputStream -> streamingService.streamDataToOutput(outputStream); } } @Service public class DataStreamingService { @PersistenceContext private EntityManager entityManager; @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) public void streamDataToOutput(OutputStream outputStream) { TypedQuery<MyEntity> query = entityManager.createQuery("SELECT e FROM MyEntity e", MyEntity.class); query.setHint(QueryHints.HINT_FETCH_SIZE, 50); Stream<MyEntity> resultStream = query.getResultStream(); resultStream.forEach(entity -> { // 写入输出流逻辑 }); } }
总结
优先推荐方案1或方案3:方案1完全掌控EntityManager生命周期,避免容器上下文干扰;方案3利用Spring事务机制自动管理,代码更简洁。方案2适合需要保留OpenEntityManagerInViewFilter原有逻辑的场景,但需注意拦截器的注册优先级。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

