首页 > 解决方案 > Spring Batch 内存泄漏 - 使用 JpaItemWriter 将 CSV 保存到数据库

问题描述

我在读取大型 CSV 文件(几百万条记录)并将记录从中保存到数据库时遇到了 Spring Batch 作业的问题。该作业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;
        }
    }

您可以entityManager.clear();在 write 方法中看到添加的内容。

作业配置:

@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();
    }

标签: javaspringspring-batch

解决方案


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

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

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

希望这可以帮助。


推荐阅读