我遇到了一个Spring Batch作业的问题,该作业需要读取一个大型CSV文件(几百万条记录)并将这些记录保存到数据库中。作业使用FlatFileItemReader
读取CSV,并使用JpaItemWriter
将已读取和已处理的记录写入数据库。问题在于,JpaItemWriter
在将另一项项目刷新到数据库后不会清除持久性上下文,并且作业最终以OutOfMemoryError
结尾。
我已经解决了问题,方法是扩展JpaItemWriter
并重写write方法,以便在写一堆之后调用EntityManager.clear()
,但是我想知道Spring Batch是否已经解决了这个问题,而问题的根源在job config中。如何正确解决这个问题?
我的解决方案:
class ClearingJpaItemWriter<T> extends JpaItemWriter<T> {
private EntityManagerFactory entityManagerFactory;
@Override
public void write(List<? extends T> items) {
super.write(items);
EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory);
if (entityManager == null) {
throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager");
}
entityManager.clear();
}
@Override
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
super.setEntityManagerFactory(entityManagerFactory);
this.entityManagerFactory = entityManagerFactory;
}
}
您可以在write方法中看到添加的
entityManager.clear();
。作业配置:
@Bean
public JpaItemWriter postgresWriter() {
JpaItemWriter writer = new ClearingJpaItemWriter();
writer.setEntityManagerFactory(pgEntityManagerFactory);
return writer;
}
@Bean
public Step appontmentInitStep(JpaItemWriter<Appointment> writer, FlatFileItemReader<Appointment> reader) {
return stepBuilderFactory.get("initEclinicAppointments")
.transactionManager(platformTransactionManager)
.<Appointment, Appointment>chunk(5000)
.reader(reader)
.writer(writer)
.faultTolerant()
.skipLimit(1000)
.skip(FlatFileParseException.class)
.build();
}
@Bean
public Job appointmentInitJob(@Qualifier("initEclinicAppointments") Step step) {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new RunIdIncrementer())
.preventRestart()
.start(step)
.build();
}
最佳答案
这是有道理的。 JpaItemWriter
(和HibernateItemWriter
)用于清除持久性上下文,但是已在BATCH-1635中将其删除(此处是the commit将其删除)。但是,已在BATCH-1759的HibernateItemWriter
中通过clearSession
参数(请参见此commit)(而不是在JpaItemWriter
中)对其进行了重新添加和配置。
因此,我建议针对Spring Batch打开一个问题,向JpaItemWriter
添加相同的选项,以便在编写项目后清除持久性上下文(这与HibernateItemWriter
一致)。
就是说,要回答您的问题,您确实可以使用自定义编写器来清除持久性上下文。
希望这可以帮助。