Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring批处理内存泄漏-使用JpaItemWriter将CSV发送到数据库_Java_Spring_Spring Batch - Fatal编程技术网

Java Spring批处理内存泄漏-使用JpaItemWriter将CSV发送到数据库

Java Spring批处理内存泄漏-使用JpaItemWriter将CSV发送到数据库,java,spring,spring-batch,Java,Spring,Spring Batch,我在Spring批处理作业中遇到了一个问题,即读取一个大的CSV文件(几百万条记录)并将其保存到数据库中。作业使用FlatFileItemReader读取CSV,并使用JpaItemWriter将读取和处理的记录写入数据库。问题是JpaItemWriter在将另一个项目块刷新到数据库后,不会清除持久性上下文,作业最终会出现OutOfMemoryError 我已经通过扩展JpaItemWriter并重写write方法解决了这个问题,这样它就可以在编写了一堆之后调用EntityManager.cle

我在Spring批处理作业中遇到了一个问题,即读取一个大的CSV文件(几百万条记录)并将其保存到数据库中。作业使用
FlatFileItemReader
读取CSV,并使用
JpaItemWriter
将读取和处理的记录写入数据库。问题是
JpaItemWriter
在将另一个项目块刷新到数据库后,不会清除持久性上下文,作业最终会出现
OutOfMemoryError

我已经通过扩展
JpaItemWriter
并重写write方法解决了这个问题,这样它就可以在编写了一堆之后调用
EntityManager.clear()
,但是我想知道Spring批处理是否已经解决了这个问题,问题的根源是否在作业配置中。如何以正确的方式解决这个问题

我的解决方案:

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;
        }
    }

这是一个正确的观点。用于清除持久性上下文的
JpaItemWriter
(和
HibernateItemWriter
)已在中删除(此处是已删除的)。但是,已通过
clearSession
参数(参见此)在中的
HibernateItemWriter
中重新添加并使其可配置,但未在
JpaItemWriter
中进行配置

因此,我建议针对Spring批处理打开一个问题,将相同的选项添加到
JpaItemWriter
中,以便在编写项目后清除持久性上下文(这将与
HibernateItemWriter
一致)

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


希望这有帮助。

这是一个正确的观点。用于清除持久性上下文的
JpaItemWriter
(和
HibernateItemWriter
)已在中删除(此处是已删除的)。但是,已通过
clearSession
参数(参见此)在中的
HibernateItemWriter
中重新添加并使其可配置,但未在
JpaItemWriter
中进行配置

因此,我建议针对Spring批处理打开一个问题,将相同的选项添加到
JpaItemWriter
中,以便在编写项目后清除持久性上下文(这将与
HibernateItemWriter
一致)

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


希望这能有所帮助。

如果您确定EM问题,那么使用
ChunkListener#afterChunk
ItemWriteListener#afterWrite
的方法可能比您的解决方案更具侵入性。检查jpa编写器代码a
EntityManager.flush
是在每次写入后执行的,所以问题不应该发生。您是否尝试了不同的(小)块大小/跳过限制?@LucaBasoricci I可能是错误的,但flush没有清除上下文。侦听器看起来确实比我的解决方案好,我只是不太了解API。我使用的跳过限制1000是作业失败前CSV中“坏”记录的适当百分比,而块大小5000是原始10k块大小的一半。这里的答案是,在执行批处理时必须调用EM.clear,因此,在处理我为此创建的大型文件时,侦听器可能是调用EM.clear的地方。感谢您的报告。如果您确定EM问题,那么使用
ChunkListener#afterChunk
ItemWriteListener#afterWrite
的方法可能比您的解决方案更具侵入性。检查jpa编写器代码a
EntityManager.flush
是在每次写入后执行的,所以问题不应该发生。您是否尝试了不同的(小)块大小/跳过限制?@LucaBasoricci I可能是错误的,但flush没有清除上下文。侦听器看起来确实比我的解决方案好,我只是不太了解API。我使用的跳过限制1000是作业失败前CSV中“坏”记录的适当百分比,而块大小5000是原始10k块大小的一半。这里的答案是,在执行批处理时必须调用EM.clear,因此,在处理我为此创建的大型文件时,侦听器可能是调用EM.clear的地方。感谢您的报告。我创建是为了在
JpaItemWriter
中添加新参数。我创建是为了在
JpaItemWriter
中添加新参数。
@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();
    }