Java 使用不同EntityManagerFactory的Spring批处理导致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 从调试来看,批处理似乎试图刷新JPAPagingItemReader doReadPage()中的JPA会话,但它在write()期间已通过(我认为)另一个以某种方式实例化的EMF刷新,因此Hibernate检测到版本号已更改并引发异常 更新 我发现了几个5年前的论坛帖子/圣歌,表明这是一个批量限制和各种解决方法。如果我想知道我是否读对了这篇文章,我将不胜感激 如果有人能指出这方面的任何明显缺陷,我将不胜感激。我已经阅读了相当多的Javadocs,但很难理解这个问题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
@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;