Java 使用不同EntityManagerFactory的Spring批处理导致org.hibernate.StaleObjectStateException

Java 使用不同EntityManagerFactory的Spring批处理导致org.hibernate.StaleObjectStateException,java,spring,hibernate,jpa,spring-batch,Java,Spring,Hibernate,Jpa,Spring Batch,环境:JDK9.0.4、Spring Boot 2.0.2和Spring Batch 4.0.1,通过Hibernate使用Postgres 9.5和JPA 我的作业使用JpaPagingItemReader和JpaItemWriter,两者之间有一个处理器,用于更新实体上的值 我的问题是,我的批处理作业在处理第一项并尝试读取下一页(区块/页面大小1)后引发org.hibernate.StaleObjectStateException 从调试来看,批处理似乎试图刷新JPAPagingItemRe

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

我的作业使用JpaPagingItemReader和JpaItemWriter,两者之间有一个处理器,用于更新实体上的值

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

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

更新

我发现了几个5年前的论坛帖子/圣歌,表明这是一个批量限制和各种解决方法。如果我想知道我是否读对了这篇文章,我将不胜感激

如果有人能指出这方面的任何明显缺陷,我将不胜感激。我已经阅读了相当多的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);
@配置
公共类DBConfig{
@自动连线
私有数据源;
@豆子
公共平台transactionManager transactionManager(EntityManager工厂emf){
JpaTransactionManager transactionManager=新的JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
返回事务管理器;
}
@豆子
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
LocalContainerEntityManagerFactoryBean em=新的LocalContainerEntityManagerFactoryBean();
em.setDataSource(数据源);
em.setPackagesToScan(“com.my.ents”、“com.my.other.ends”);
JpaVendorAdapter=new HibernateJpaVendorAdapter();
em.setjpavendor适配器(供应商适配器);
返回em;
}
}
@豆子
@步进镜
公共JpaPagingItemReader getItemReader(
@值(“#{jobParameters['council']}”)字符串councilShortName,
@值(“#{jobParameters['scrapeType']}”)字符串scrapeTypeString){
如果(councilShortName==null){
抛出新的NullPointerException(“councilShortName不能为null”);
}
if(scrapeTypeString==null){
抛出新的NullPointerException(“scrapeType不能为null”);
}
JpaPagingItemReader itemReader=新的JpaPagingItemReader();
itemReader.setEntityManagerFactory(entityManagerFactory);

@Bean
public JpaItemWriter itemWriterScrapeHistorySingle(){
JpaItemWriter itemWriter=新的JpaItemWriter();
itemWriter.setEntityManagerFactory(entityManagerFactory);
返回项目编写器;
}
@配置
@启用批处理
@组成部分
公共类CustomBatchConfigurer扩展了DefaultBatchConfigurer{
私有静态最终日志记录器=LogFactory.getLog(CustomBatchConfigurer.class);
@自动连线
私有线程池taskExecutor taskExecutor;
@自动连线
私人工作登记处;
@自动连线
私有数据源;
@限定符(“transactionManager”)
@自动连线
私有平台transactionManager transactionManager;

您是唯一一个使用服务器的人,还是在批处理过程运行时发生并发请求?如果是后者,则独立请求可能已更新了一行。@gagansingh感谢您的评论;在这种情况下,我是唯一一个使用服务器的人;这是一个本地容器化开发环境。我已更新了问题我想我已经在SpringBatch中发现了一些已知的限制,可以解释这一点。
@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;