Spring Batch с использованием различных EntityManagerFactories, вызывающих org.hibernate.StaleObjectStateException - PullRequest
0 голосов
/ 06 июня 2018

Среда: JDK9.0.4, Spring Boot 2.0.2 с Spring Batch 4.0.1, с использованием Postgres 9.5 и JPA через Hibernate.

Моя работа использует JpaPagingItemReader и JpaItemWriter с процессором, который обновляет значения всущность.

Моя проблема заключается в том, что мое пакетное задание создает исключение org.hibernate.StaleObjectStateException после обработки первого элемента и попытки прочитать следующую страницу (кусок / размер страницы 1)

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

Из отладки выясняется, что Batch пытается сбросить JPA-сессию в JPAPagingItemReader doReadPage (), но он уже был сброшен во время write () через (я думаю) другую EMF, которая каким-то образом была создана, и поэтому Hibernate обнаруживает, что номер версии изменилсяи выдает исключение.

Обновление

Я нашел пару 5-летних сообщений на форуме / JIRA, которые предполагают, что это ограничение в пакетном режиме и разнообразиеобходных путей.Буду признателен за мысли о том, правильно ли я читаю.

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

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

http://forum.spring.io/forum/spring-projects/batch/92792-jpapagingitemreader-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;
...