Spring JPA定时任务中懒加载集合触发LazyInitializationException问题
问题原因与解决方案
核心问题:@Transactional注解未生效
你在CSPRetry内部类的run方法上加的@Transactional完全没起作用。Spring的事务注解基于代理机制实现,只有被Spring容器管理的Bean(比如标注@Service、@Component的类),其方法上的事务注解才会被识别并生成代理。而CSPRetry是CSPService的内部类,通过new CSPRetry(...)手动实例化,不在Spring管理范围内,所以注解等于无效。
你看到的“字段已填充”只是findById执行查询捞取了主实体数据,但此时JPA的Session已经关闭,懒加载的zuege集合仍处于未初始化的代理状态,访问自然抛出LazyInitializationException。
可行解决方案
方案1:将CSPRetry改为Spring托管Bean
把内部类抽成独立的Spring组件,让Spring管理其生命周期,事务注解就能正常生效:
// 独立的CSPRetry组件 @Service public class CSPRetry implements Runnable { @Autowired private VorgangRepository vorgangRepository; // 构造函数传入所需参数 public CSPRetry(...) { // 初始化参数 } @Override @Transactional public void run() { // 事务生效,懒加载集合可正常初始化 vorgangRepository.findById(55).get().getZuege().size(); } } // 修改CSPService,通过Spring获取CSPRetry实例 @Service public class CSPService { @Autowired private VorgangRepository vorgangRepository; @Autowired private ApplicationContext applicationContext; private ThreadPoolTaskScheduler threadPoolExecuter; @PostConstruct private void init() { threadPoolExecuter = new ThreadPoolTaskScheduler(); threadPoolExecuter.setPoolSize(10); threadPoolExecuter.setThreadNamePrefix("CSPRetryTaskScheduler"); threadPoolExecuter.setDaemon(true); threadPoolExecuter.initialize(); } public void retry() { // 从Spring容器获取实例,而非手动new CSPRetry retryTask = applicationContext.getBean(CSPRetry.class, ...); threadPoolExecuter.schedule(retryTask, LocalDateTime.now().plusSeconds(30).toInstant(OffsetDateTime.now().getOffset())); } }
方案2:编程式控制事务
如果不想调整组件结构,用TransactionTemplate手动管理事务边界,无需依赖注解:
@Service public class CSPService { @Autowired private VorgangRepository vorgangRepository; @Autowired private TransactionTemplate transactionTemplate; private ThreadPoolTaskScheduler threadPoolExecuter; private class CSPRetry implements Runnable { @Override public void run() { // 用TransactionTemplate包裹事务逻辑 transactionTemplate.execute(status -> { // 处于事务中,Session保持打开,懒加载正常 vorgangRepository.findById(55).get().getZuege().size(); return null; }); } } // 其他代码不变... }
方案3:主动关联查询,避免懒加载
直接在查询时把关联的zuege集合一起查出,彻底绕开懒加载问题:
// 修改VorgangRepository,添加自定义查询方法 public interface VorgangRepository extends CrudRepository<VorgangDO, Long> { @Query("SELECT v FROM VorgangDO v LEFT JOIN FETCH v.zuege WHERE v.id = :id") Optional<VorgangDO> findByIdWithZuege(@Param("id") Long id); } // 在run方法中调用该方法 @Override public void run() { // 一次性查出主实体和关联集合,无需懒加载 vorgangRepository.findByIdWithZuege(55).get().getZuege().size(); }
总结
最根本的问题是Spring事务注解无法作用于非Spring托管对象,优先用方案1或方案2保证事务生效,或用方案3彻底规避懒加载场景。
内容的提问来源于stack exchange,提问作者TomHalo
相关产品推荐
相关产品推荐

