You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch的ItemProcessor执行时出现Hibernate no session异常如何解决

Spring Batch ItemProcessor中访问Hibernate实体关联出现懒加载异常

我在运行Spring Batch作业时,在ItemProcessor中访问Hibernate实体关联关系时遇到了问题。该ItemProcessor属于基于chunk的步骤,按我理解ItemProcessor是在事务中运行的,因此应该可以懒加载实体关联关系。

问题详情

我在执行ItemProcessor逻辑时收到如下异常:

org.hibernate.LazyInitializationException: could not initialize proxy [org.powo.model.registry.Organisation#1] - no Session
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:169)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:309)
        at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
        at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
        at org.powo.model.registry.Organisation$HibernateProxy$OFnEWoXa.getIdentifier(Unknown Source)
        at org.powo.model.solr.BaseSolrInputDocument.build(BaseSolrInputDocument.java:39)
        at org.powo.model.solr.TaxonSolrInputDocument.<init>(TaxonSolrInputDocument.java:67)
        at org.powo.model.Taxon.toSolrInputDocument(Taxon.java:1091)
        at org.powo.job.reindex.TaxonToSolrInputDocumentProcessor.process(TaxonToSolrInputDocumentProcessor.java:20)
        at org.powo.job.reindex.TaxonToSolrInputDocumentProcessor.process(TaxonToSolrInputDocumentProcessor.java:13)
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:126)
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:303)
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:202)
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:272)
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375)
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145)
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
        at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:66)
        at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
        at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)
        at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
        at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:136)
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:308)
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:141)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:834)

以下是用到的ItemProcessor代码供参考(遍历实体关联关系的逻辑位于toSolrInputDocument方法内):

@Component
public class TaxonToSolrInputDocumentProcessor implements ItemProcessor<Taxon, SolrInputDocument> {
  @Autowired
  private ApplicationContext context;

  @Override
  public SolrInputDocument process(Taxon item) throws Exception {
    return item.toSolrInputDocument(context);
  }
}

我使用的读取器是org.springframework.batch.item.database.HibernatePagingItemReader。

已尝试的解决方案

我已经尝试过以下方案,但都没有解决上述报错:

  • 改用JpaPagingItemReader替代HibernatePagingItemReader,问题依旧存在
  • 通过@Autowired注入SessionFactory,在遍历实体关联关系的代码前后执行openSession/closeSession操作

受数据模型限制,我无法在单次查询中fetch所有关联关系,因此需要使用有状态的session(不过部分关联我是希望可以提前fetch的)。


问题根因

分页类ItemReader的默认实现中,每一页数据读取完成后就会关闭对应的Session/EntityManager,读取到的实体就变为脱管状态。虽然ItemProcessor确实运行在Chunk的事务中,但这个事务对应的是新的持久上下文,和读取实体时的上下文无关,因此访问未加载的代理对象就会抛出无Session的懒加载异常。

之前的尝试不生效的原因:

  • 切换JpaPagingItemReader没用:它的分页逻辑和HibernatePagingItemReader一致,同样会在分页读取完成后关闭EntityManager
  • 手动打开Session没用:新打开的Session中没有读取出来的实体的代理上下文,代理对象关联的是已经关闭的旧Session,新Session无法识别这个代理,因此还是会报错

解决方案

方案1(优先推荐,性能最优)

尽可能在Reader的查询语句中使用fetch join提前加载你确定会用到的关联,比如:

from Taxon t left join fetch t.organisation left join fetch t.otherCommonField

能提前加载的关联都提前加载,减少运行时懒加载的开销,剩余少量无法提前fetch的关联可以搭配方案2使用。

方案2(适配无法全量fetch的场景)

将Hibernate Session绑定到整个Chunk的执行生命周期,让读、处理、写全流程使用同一个Session。实现方式如下:

  1. 注册OpenSessionInterceptor Bean
import org.springframework.orm.hibernate5.support.OpenSessionInterceptor;

@Bean
public OpenSessionInterceptor openSessionInterceptor(SessionFactory sessionFactory) {
    OpenSessionInterceptor interceptor = new OpenSessionInterceptor();
    interceptor.setSessionFactory(sessionFactory);
    return interceptor;
}
  1. 将拦截器添加到对应Step的通知链中
@Bean
public Step reindexStep(JobRepository jobRepository, PlatformTransactionManager transactionManager,
                       HibernatePagingItemReader<Taxon> reader, 
                       TaxonToSolrInputDocumentProcessor processor,
                       ItemWriter<SolrInputDocument> writer,
                       OpenSessionInterceptor openSessionInterceptor) {
    return new StepBuilder("reindexStep", jobRepository)
            .<Taxon, SolrInputDocument>chunk(100, transactionManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .interceptor(openSessionInterceptor)
            .build();
}

如果使用JPA则对应替换为OpenEntityManagerInViewInterceptor,注入EntityManagerFactory即可,配置逻辑完全一致。

配置完成后整个Chunk执行过程中Session会保持打开状态,ItemProcessor中访问关联时就可以正常懒加载了。


内容的提问来源于stack exchange,提问作者Malcolm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 02:36:00