简体   繁体   English

在 ItemProcessor (Spring Batch) 中休眠没有会话

[英]Hibernate no session in ItemProcessor (Spring Batch)

I'm having an issue with trying to access Hibernate entity relationships as part of an ItemProcessor while running a Spring Batch job.在运行 Spring Batch 作业时,我在尝试访问 Hibernate 实体关系作为ItemProcessor一部分时遇到问题。 The ItemProcessor is part of a chunk-based step. ItemProcessor是基于块的步骤的一部分。 As far as I can tell the ItemProcessor runs in a transaction and therefore should be able to lazily load entity relationships.据我所知ItemProcessor在事务中运行,因此应该能够延迟加载实体关系。

The issue问题

I'm getting the following exception as part of the ItemProcessor logic:作为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)

Here's the ItemProcessor for reference (and within the toSolrInputDocument logic is where the entity relationships are traversed):这是供参考的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);
  }
}

And I'm using an org.springframework.batch.item.database.HibernatePagingItemReader as the reader.我使用org.springframework.batch.item.database.HibernatePagingItemReader作为读者。

What I've tried我试过的

I've tried the following but none of the solutions have prevented the error above:我已经尝试了以下但没有一个解决方案可以防止上述错误:

  • using a JpaPagingItemReader instead of HibernatePagingItemReader but this still has the same issue使用JpaPagingItemReader而不是HibernatePagingItemReader但这仍然有同样的问题
  • using @Autowired to get a SessionFactory and then doing openSession / closeSession around the code which traverses the entity relationships使用@Autowired获取SessionFactory ,然后围绕遍历实体关系的代码执行openSession / closeSession

Because of the data model I'm not able to fetch all relationships in one query so I need to use a stateful session (though I would like to fetch some!).由于数据模型的原因,我无法在一个查询中获取所有关系,因此我需要使用有状态会话(尽管我想获取一些!)。

AFAIK every step (read, process, write) creates a separate transaction for every chunk that is processed, so if your ItemReader does not initialize an association that is needed in the processor or writer, you will run into this error because the entities become detached when the entity manager is closed after the transaction commit. AFAIK 每一步(读取、处理、写入)都会为处理的每个块创建一个单独的事务,因此如果您的ItemReader没有初始化处理器或ItemReader器中所需的关联,您将遇到此错误,因为实体分离当事务提交后实体管理器关闭时。 I don't know if it is possible to tell Spring to keep an entity manager around for the whole process, but if you want to rely on lazy initialization, you will have to figure this out.我不知道是否可以告诉 Spring 在整个过程中保留一个实体管理器,但是如果您想依赖延迟初始化,则必须弄清楚这一点。

I would recommend you to make sure that you use proper join fetches though, to avoid the need for lazy initialization ie select t from Taxon t join fetch t.organization o , or even better, use a DTO approach so that you don't have to care about lazy initialization at all.我建议您确保使用正确的连接提取,以避免延迟初始化的需要,即select t from Taxon t join fetch t.organization o ,或者甚至更好,使用 DTO 方法,以便您没有根本不关心延迟初始化。

I think this is a perfect use case forBlaze-Persistence Entity Views .我认为这是Blaze-Persistence Entity Views的完美用例。

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids.我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义的模型之间轻松映射,例如类固醇上的 Spring Data Projections。 The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(Taxon.class)
public interface TaxonDto {
    @IdMapping
    Long getId();
    String getName();
    OrganizationDto getOrganization();

    @EntityView(Organization.class)
    interface OrganizationDto {
        @IdMapping
        Long getId();
        String getName();
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.查询是将实体视图应用于查询的问题,最简单的只是按 id 查询。

TaxonDto a = entityViewManager.find(entityManager, TaxonDto.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features Spring Data 集成允许您几乎像 Spring Data Projections 一样使用它: https : //persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<TaxonDto> findAll(Pageable pageable);

The best part is, it will only fetch the state that is actually necessary!最好的部分是,它只会获取实际需要的状态!

I've now resolved this - in the end changing to JpaPagingItemReader did the trick.我现在已经解决了这个问题 - 最后JpaPagingItemReader解决了这个问题。 I'm not sure why it didn't work previously, although I did find that setting any kind of taskExecutor did seem to immediately break the lazy loading.我不确定为什么它以前不起作用,尽管我确实发现设置任何类型的taskExecutor似乎会立即破坏延迟加载。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM