首页 > 解决方案 > Spring Batch 使用不同的 EntityManagerFactories 导致 org.hibernate.StaleObjectStateException

问题描述

环境:JDK9.0.4,Spring Boot 2.0.2 和 Spring Batch 4.0.1,通过 Hibernate 使用 Postgres 9.5 和 JPA。

我的工作使用 JpaPagingItemReader 和 JpaItemWriter 与处理器之间更新实体上的值。

我的问题是我的批处理作业在处理第一项并尝试读取下一页(块/页面大小 1)后抛出 org.hibernate.StaleObjectStateException

Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.my.entity#1315]

从调试来看,Batch 似乎试图在 JPAPagingItemReader doReadPage() 中刷新 JPA 会话,但它已经在 write() 期间通过(我认为)另一个以某种方式实例化的 EMF 刷新,因此 Hibernate 检测到版本号已更改并抛出例外。

更新

我发现几个 5 年前的论坛帖子/JIRA 表明这是 Batch 的限制和各种解决方法。对于我是否正确阅读本文的想法,我将不胜感激。

https://jira.spring.io/browse/BATCH-1110

https://jira.spring.io/browse/BATCH-1166

http://forum.spring.io/forum/spring-projects/batch/92792-jpapaagingitemreader-stale-object-exception

如果有人能指出这方面的任何明显缺陷,我将不胜感激。我已经阅读了大量的 Javadocs,但很难理解这个问题。

@Configuration
public class DBConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);

        return transactionManager;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.my.ents", "com.my.other.ends");

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);

        return em;
    }

}



@Bean
@StepScope
public JpaPagingItemReader<ScrapeHistorySingle> getItemReader(
        @Value("#{jobParameters['council']}") String councilShortName,
        @Value("#{jobParameters['scrapeType']}") String scrapeTypeString) {

    if (councilShortName == null) {
        throw new NullPointerException("councilShortName cannot be null");
    }

    if (scrapeTypeString == null) {
        throw new NullPointerException("scrapeType cannot be null");
    }

    JpaPagingItemReader<ScrapeHistorySingle> itemReader = new JpaPagingItemReader<>();

    itemReader.setEntityManagerFactory(entityManagerFactory);

...

@Bean
public JpaItemWriter<ScrapeHistorySingle> itemWriterScrapeHistorySingle() {

    JpaItemWriter<ScrapeHistorySingle> itemWriter = new JpaItemWriter<>();

    itemWriter.setEntityManagerFactory(entityManagerFactory);

    return itemWriter;
}


@Configuration
@EnableBatchProcessing
@Component
public class CustomBatchConfigurer extends DefaultBatchConfigurer {

    private static final Log logger = LogFactory.getLog(CustomBatchConfigurer.class);

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private JobRegistry jobRegistry;

    @Autowired
    private DataSource dataSource;

    @Qualifier("transactionManager")
    @Autowired
    private PlatformTransactionManager transactionManager;

标签: javaspringhibernatejpaspring-batch

解决方案


推荐阅读