在Async方法中启动Spring Batch作业抛出懒加载异常如何解决
问题根因
- 懒加载异常本质是跨线程Session丢失:
RepositoryItemReader读取数据时的Session绑定在chunk事务所属的线程上,你使用AsyncItemProcessor将处理逻辑提交到了独立的线程池执行,此时reader对应的事务已经提交、Session已关闭,processor中访问未初始化的懒加载关联集合就会触发报错。 - 启动Job的方法加
@Transactional报错是Spring Batch的硬性限制:JobRepository会自行管理事务,不允许调用JobLauncher的上层方法存在已开启的事务,因此不能在migrateAttachments方法上加事务注解。
可行解决方案(按优先级排序)
方案1:修改查询逻辑主动加载关联集合(最推荐)
直接在MessageRepository的findAllByTaskIdIsNotNull方法上新增关联抓取逻辑,读取数据时就把要用到的懒加载集合初始化,彻底规避跨线程懒加载问题。示例代码如下:
@Query("SELECT m FROM MessageDB m LEFT JOIN FETCH m.你报错的懒加载集合属性名 WHERE m.taskId IS NOT NULL") List<MessageDB> findAllByTaskIdIsNotNull();
不需要调整其他Batch配置,改完直接生效,性能损耗最小。
方案2:改用Step级多线程代替AsyncItemProcessor
AsyncItemProcessor的跨线程场景坑较多,直接把多线程配置放到Step层面,让整个chunk的读、处理、写都在同一个异步线程中执行,Session和事务会和当前线程绑定,不会出现Session丢失问题。
修改点如下:
- 把
AsyncItemProcessor、AsyncItemWriter的配置删掉,换回普通的MessageDbAttachmentsProcessor和RepositoryItemWriter - 调整Step的配置,新增线程池配置:
@Bean public Step step() { return stepBuilderFactory.get("step") .<MessageDB, MessageDB>chunk(5) .reader(reader()) .processor(messageDbAttachmentsProcessor) .writer(writer()) .transactionManager(platformTransactionManager) // 新增Step级多线程配置 .taskExecutor(taskExecutor()) .build(); }
方案3:Processor层新增事务绑定Session
如果不想改上面的配置,可以给MessageDbAttachmentsProcessor的处理方法加上@Transactional(propagation = Propagation.REQUIRES_NEW),处理时会开启新的Session,访问关联集合前先把detached状态的实体合并到当前Session即可:
@Transactional(propagation = Propagation.REQUIRES_NEW) @Override public MessageDB process(MessageDB item) throws Exception { MessageDB merged = messageRepository.save(item); // 或者用entityManager.merge(item) // 后续操作都用merged对象访问懒加载集合 // ... 你的原有业务逻辑 }
该方案需要额外的merge操作,性能相对前两个方案差,适合临时修复场景。
内容的提问来源于stack exchange,提问作者zafirov
相关产品推荐
相关产品推荐

