java - Spring Batch 内存泄漏 - 使用 JpaItemWriter 将 CSV 写入数据库

标签 java spring spring-batch

我在使用 Spring Batch 作业读取大型 CSV 文件(几百万条记录)并将记录保存到数据库时遇到问题。该作业使用 FlatFileItemReader 读取 CSV,使用 JpaItemWriter 将读取和处理的记录写入数据库。问题是,在将另一 block 项目刷新到数据库后,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();
    }

最佳答案

这是一个有效的观点。 JpaItemWriter(和 HibernateItemWriter)用于清除持久上下文,但这已在 BATCH-1635 中删除。 (这里是 the commit 删除了它)。然而,它已被重新添加并在 BATCH-1759 中的 HibernateItemWriter 中进行配置。通过 clearSession 参数(请参阅此 commit ),但不在 JpaItemWriter 中。

因此,我建议针对 Spring Batch 提出一个问题,将相同的选项添加到 JpaItemWriter 中,以便在写入项目后清除持久性上下文(这与 HibernateItemWriter)。

也就是说,为了回答您的问题,您确实可以像您一样使用自定义编写器来清除持久性上下文。

希望这有帮助。

关于java - Spring Batch 内存泄漏 - 使用 JpaItemWriter 将 CSV 写入数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54746793/

相关文章:

java - Spring Batch - 是否可以在 FlatFileReader 中有一个动态列?

java - 如何阻止 Hibernate 急切地获取多对一关联对象

spring - 通过maven生成JPA 2.0元模型而不需要persistence.xml?

java - 使用 hibernate 检查数据库重复的优化方法

java - Spring Autowire 属性对象

java - 在spring-batch中动态配置Job

spring批量读写执行时间

java - 为什么我的改造后请求不起作用?

java - ClassNotFoundException 与 XposedBridge

java - URL 的解析 Intent 有时为 null,抛出 NullPointerException