java - 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;
解决方案
推荐阅读
- python - 用列表填充数组元素
- python - 将 __main__ 中的全局变量访问权限授予 pytest 会话
- jquery - jQuery:仅在奇数时选择最后一个 div
- javascript - 导入 CSV 后如何修改列标题?
- javascript - 弹出键盘时网页滚动
- java - 如何在JAVA JPA Spring Boot中的一个SQL查询中选择多个数据
- visual-studio-code - VS Code:在当前文件中而不是在任何文件中“转到下一个问题”?
- ios - Swift 4:NSNotifcationCenter 通知的间歇性重复
- python - 机器人框架打开 Tkinter 的多个窗口
- python - 一旦向其中添加项目的函数完成执行,Python 字典就会变为空