集成测试报Session/EntityManager关闭异常但应用正常运行如何解决
异常触发原因
第一个异常:IllegalStateException: No transactional EntityManager available
默认通过@PersistenceContext注入的是事务作用域的EntityManager,仅当存在活跃事务上下文时才会完成初始化、处于可用状态。
应用正常运行时,调用formCriteria的上层业务逻辑已经开启了事务,因此EntityManager可以正常使用;而集成测试的调用路径默认没有开启事务上下文,未激活的EntityManager调用unwrap方法就会抛出该异常。
第二个异常:IllegalStateException : Session/EntityManager closed
标注在ReportsUtil类上的@Transactional注解,会为formCriteria方法创建独立事务:事务在方法执行前开启,方法执行结束后自动提交/回滚,同时绑定的EntityManager也会被同步关闭。
你在formCriteria方法内创建的Criteria实例,是和当前事务绑定的Session/EntityManager强关联的,当方法返回、事务结束后,EntityManager已经被回收关闭,后续在ReportRDBMS的executeQueryForSuccess方法中调用criteria.list()时,操作的是已经关闭的Session,因此抛出该异常。
应用正常运行时上层逻辑已经存在事务,ReportsUtil的@Transactional默认会加入已有事务,方法执行结束后不会关闭上层事务绑定的EntityManager,因此不会触发该问题。
解决方案
- 调整事务边界:移除
ReportsUtil类上的@Transactional注解,将@Transactional注解添加到包含完整查询流程的上层方法(即ReportRDBMS的executeQueryForSuccess方法),保证从创建Criteria到执行查询的全流程都处于同一个活跃事务中,EntityManager不会提前关闭。 - 调整EntityManager作用域:如果业务场景需要在事务外持有EntityManager,可在注入时指定扩展作用域,修改注入注解为
@PersistenceContext(unitName = "logs", type = PersistenceContextType.EXTENDED),扩展作用域的EntityManager生命周期和当前持久化上下文绑定,不会随事务结束自动关闭,注意该类型的EntityManager不是线程安全的,不可跨线程共享。 - 集成测试补充事务配置:如果仅需要解决集成测试的问题,不需要修改业务代码,可直接在集成测试类/测试方法上添加
@Transactional注解,让整个测试逻辑运行在统一的事务上下文中,Spring测试默认会在测试结束后自动回滚事务,不会产生脏数据。 - 封装查询逻辑:修改
ReportsUtil的设计,不要返回未执行的Criteria实例,将条件拼接、查询执行的逻辑都封装到ReportsUtil的事务方法内,直接返回最终查询结果,避免外部持有已关闭Session关联的Criteria实例。
内容的提问来源于stack exchange,提问作者Malay Pandey
相关产品推荐
相关产品推荐

