Spring Boot下Hibernate LAZY懒加载失效报no Session异常排查
问题根因定位
你遇到的懒加载初始化异常,是三个配置/逻辑层面的问题叠加导致的:
- 事务管理器Bean名称错配,事务未真正生效
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.model.Fund.fundAlternateIds, could not initialize proxy - no Session
你在数据库配置类中定义的事务管理器Bean的实际注册名称是sleeveTransactionManager,但在两个核心位置都引用了不存在的myTransactionManager:@EnableJpaRepositories配置项的transactionManagerRef = "myTransactionManager"FundServiceImpl类上@Transactional("myTransactionManager")
名称不匹配导致Spring无法找到对应的事务管理器,声明式事务切面完全失效。整个查询流程没有绑定Hibernate持久化Session,查询返回的Fund对象的关联集合都是未初始化的动态代理。
- 事务边界与JSON序列化阶段完全脱节
就算修正了名称匹配问题,Spring声明式事务的默认边界仅覆盖Service方法内部逻辑。当Service方法把查询到的List<Fund>返回给控制层时,事务就会提交,绑定的Hibernate Session会被直接关闭。
而你在Fund实体的fundAlternateIds字段上加了@JsonManagedReference注解,强制Jackson在序列化返回值时遍历读取这个字段。此时要初始化懒加载的代理集合,必须依托已经被关闭的Session,就会触发你看到的JsonMappingException序列化异常,对应栈中的引用链Collections$UnmodifiableRandomAccessList[0]->Fund["fundAlternateIds"]。 - 事务管理器的EntityManagerFactory绑定存在逻辑缺陷
你当前的事务管理器实现里,手动调用getEntityManagerFactory(getDataSource()).getObject()构造实例,没有通过依赖注入的方式复用Spring容器托管的EntityManagerFactory,就算修正了名称匹配问题,也可能出现事务管理器和实际持久化逻辑使用的不是同一个Session工厂的问题,导致Session上下文完全不连通。
修正实现方案
第一步:修复基础配置错误(必须执行)
统一事务管理器的命名和绑定逻辑:
@Configuration @EnableJpaRepositories( basePackages = {"com.example.repository"}, entityManagerFactoryRef = "myEntityManagerFactory", // 保持和Bean注册名一致 transactionManagerRef = "myTransactionManager" ) @EnableTransactionManagement public class ApplicationDatabaseConfiguration { // 其他原有Bean配置保持不变... // 把原来的sleeveTransactionManager Bean名改成myTransactionManager @Bean(name = "myTransactionManager") public PlatformTransactionManager transactionManager( // 直接通过依赖注入获取容器托管的EMF实例,不要手动构造 @Qualifier("myEntityManagerFactory") LocalContainerEntityManagerFactoryBean emFactoryBean, @Qualifier("postGresDataSource") HikariDataSource dataSource ) { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(emFactoryBean.getObject()); tm.setDataSource(dataSource); return tm; } }
第二步:根据业务需求匹配懒加载序列化策略
策略1:接口不需要返回关联的FundAlternateId数据(最匹配LAZY配置的设计目标)
- 移除
Fund实体中fundAlternateIds字段上的@JsonManagedReference注解 - 直接给该字段加上
@JsonIgnore注解,让序列化流程完全跳过该字段 - 这种模式下序列化阶段不会访问懒加载代理,哪怕Session已关闭也不会触发异常,同时保留了事务范围内手动访问关联集合做业务校验的灵活性。
策略2:接口必须返回关联的FundAlternateId数据
- 推荐实现:在事务范围内一次性加载关联数据
直接在Repository层通过JOIN FETCH语法关联查询,避免序列化阶段触发懒加载:
修改Service层的查询逻辑调用这个方法,整个加载过程都在事务范围内完成,返回的实体关联集合已经是初始化完成的普通数据对象,序列化阶段不需要再访问Hibernate Session。public interface FundRepository extends PagingAndSortingRepository<Fund, String> { // 新增带关联加载的查询方法 @Query("SELECT f FROM fund f LEFT JOIN FETCH f.fundAlternateIds") List<Fund> findAllWithAlternateIds(); } - 兜底实现:在Service方法返回前手动初始化懒加载集合
注意这种方式在分页数据量大的时候会触发N+1查询问题,需要严格控制单次返回的Fund数量。@Override public List<Fund> getAllPublicFunds(Integer pageNo, Integer pageSize, String sortBy) { List<Fund> fundList = fundDao.getAllPublicFunds(pageNo, pageSize, sortBy); // 在事务生效范围内强制初始化懒加载集合 fundList.forEach(fund -> Hibernate.initialize(fund.getFundAlternateIds())); return fundList; } - 快速但不推荐的方案:开启Open Session in View模式,把Hibernate Session的生命周期延伸到整个Web请求完成阶段。这种方式会长期占用数据库连接,容易引发连接池耗尽、慢查询等生产问题,不建议长期使用。
内容的提问来源于stack exchange,提问作者Pritam Bohra
相关产品推荐
相关产品推荐

