【发布时间】:2019-02-18 11:57:00
【问题描述】:
我在使用 Spring Batch 作业读取大型 CSV 文件(几百万条记录)并将记录从中保存到数据库时遇到问题。该作业使用FlatFileItemReader 读取CSV,使用JpaItemWriter 将读取和处理的记录写入数据库。问题是JpaItemWriter 在将另一块项目刷新到数据库后没有清除持久性上下文,作业以OutOfMemoryError 结束。
我已经通过扩展 JpaItemWriter 并覆盖 write 方法解决了这个问题,以便它在编写一堆之后调用 EntityManager.clear(),但我想知道 Spring Batch 是否已经解决了这个问题,问题的根源在于作业配置。如何正确解决这个问题?
我的解决方案:
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();
}
【问题讨论】:
-
如果您确定 EM 问题,可能使用
ChunkListener#afterChunk或ItemWriteListener#afterWrite的方法比您的解决方案更具侵入性。每次写入后都会检查 jpa-writer 代码EntityManager.flush,因此不应发生问题。您是否尝试过不同的(小)块大小/跳过限制? -
@LucaBassoRicci 我可能错了,但刷新并不能清除上下文。听众确实看起来比我的解决方案更好,我只是不太了解 API。在作业失败之前,我使用的 1000 的跳过限制是 CSV 中“坏”记录的适当百分比,并且 5000 的块大小比原始 10k 块小一半。此处stackoverflow.com/questions/13886608/… 的答案表示,在进行批处理时必须调用 EM.clear,因此在处理大文件时,侦听器可能是调用 EM.clear 的地方
-
我为此创建了jira.spring.io/browse/BATCH-2797。感谢您的报告。
标签: java spring spring-batch