Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.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
Spring boot Spring批处理/数据JPA应用程序在调用JPA存储库(save,saveAll)方法时未将数据持久化/保存到Postgres数据库_Spring Boot_Hibernate_Jpa_Transactions_Spring Batch - Fatal编程技术网

Spring boot Spring批处理/数据JPA应用程序在调用JPA存储库(save,saveAll)方法时未将数据持久化/保存到Postgres数据库

Spring boot Spring批处理/数据JPA应用程序在调用JPA存储库(save,saveAll)方法时未将数据持久化/保存到Postgres数据库,spring-boot,hibernate,jpa,transactions,spring-batch,Spring Boot,Hibernate,Jpa,Transactions,Spring Batch,我快不知所措了。到目前为止,我阅读/谷歌搜索了无数次,并在所有谷歌/stackoverflow帖子上尝试了解决方案,这些帖子都有类似的问题(有很多)。有些似乎很有希望,但对我来说什么都不起作用;虽然我已经取得了一些进展,我相信我走在了正确的轨道上(我相信在这一点上它与事务管理器有关,并且与SpringBatch与SpringDataJPA可能存在一些冲突) 参考文献: 与前面提到的帖子类似,我有一个Spring Boot应用程序,它使用Spring批处理和Spring数据JPA。它从.csv

我快不知所措了。到目前为止,我阅读/谷歌搜索了无数次,并在所有谷歌/stackoverflow帖子上尝试了解决方案,这些帖子都有类似的问题(有很多)。有些似乎很有希望,但对我来说什么都不起作用;虽然我已经取得了一些进展,我相信我走在了正确的轨道上(我相信在这一点上它与事务管理器有关,并且与SpringBatch与SpringDataJPA可能存在一些冲突)

参考文献:

  • 与前面提到的帖子类似,我有一个Spring Boot应用程序,它使用Spring批处理Spring数据JPA。它从
    .csv
    文件中读取逗号分隔的数据,然后进行一些处理/转换,并尝试使用JPA存储库方法持久化/保存到数据库,特别是这里的
    .saveAll()
    (我也尝试了
    .save()
    方法,这也做了同样的事情),因为我正在保存用户定义的数据类型(批插入)的
    列表

    现在,我的代码在Spring Boot starter
    1.5.9.RELEASE
    上运行良好,但我最近尝试升级到2.X.X,经过无数个小时的调试,我发现只有版本
    2.2.0.RELEASE
    将数据保存到数据库。因此升级到>=
    2.2.1.RELEASE
    会破坏持久性。从
    .csv
    中读取的所有内容都很好,就在代码流第一次命中JPA存储库方法时,如
    .save()
    .saveAll()
    ,应用程序保持运行,但没有任何内容被持久化。我还注意到Hikari池日志
    “active=1 idle=4”
    ,但当我在版本
    1.5.9.RELEASE
    上查看同一日志时,它会在持久化数据后立即显示
    active=0 idle=5
    ,因此应用程序肯定是挂起的。我进入调试器,甚至看到在跳入存储库调用之后,它通过Spring AOP库等(都是第三方)进入了一个几乎无限的循环,我认为永远不会回到我编写的真正的应用程序/业务逻辑

    3c22fb53ed64 2021-05-20 23:53:43.909 DEBUG
                        [HikariPool-1 housekeeper] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=5, active=1, idle=4, waiting=0)
    
    无论如何,我尝试了对其他人有效的最常见的解决方案:

  • 定义
    JpaTransactionManager
    @Bean
    并将其注入
    步骤
    函数,同时使用
    平台TransactionManager
    保留
    作业存储库
    。这不起作用。然后我还尝试在JobRepository
    @Bean
    中使用
    JpaTransactionManager
    ,这也不起作用
  • 在我的应用程序中定义一个
    @RestController
    端点来手动触发此作业,而不是从我的主
    application.java
    类手动执行。(我将在下面详细讨论这一点)。根据我上面发布的一篇文章,即使在spring>=2.2.1上,数据也正确地保存到了数据库中,我进一步怀疑spring批处理持久化/实体/事务管理器的某些方面出了问题
  • 代码基本上是这样的: BatchConfiguration.java

    @Configuration
    @EnableBatchProcessing
    @Import({DatabaseConfiguration.class})
    public class BatchConfiguration {
    
    // Datasource is a Postgres DB defined in separate IntelliJ project that I add to my pom.xml
    DataSource dataSource;
    
    @Autowired
    public BatchConfiguration(@Qualifier("dataSource") DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    @Bean
    @Primary
    public JpaTransactionManager jpaTransactionManager() {
        final JpaTransactionManager tm = new JpaTransactionManager();
        tm.setDataSource(dataSource);
        return tm;
    }
    
    
     @Bean
     public JobRepository jobRepository(PlatformTransactionManager transactionManager) throws Exception {
        JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
        jobRepositoryFactoryBean.setDataSource(dataSource);
        jobRepositoryFactoryBean.setTransactionManager(transactionManager);
        jobRepositoryFactoryBean.setDatabaseType("POSTGRES");
        return jobRepositoryFactoryBean.getObject();
    }
    
    @Bean
    public JobLauncher jobLauncher(JobRepository jobRepository) {
        SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
        simpleJobLauncher.setJobRepository(jobRepository);
        return simpleJobLauncher;
    }
    
    @Bean(name = "jobToLoadTheData")
     public Job jobToLoadTheData() {
        return jobBuilderFactory.get("jobToLoadTheData")
                .start(stepToLoadData())
                .listener(new CustomJobListener())
                .build();
    }
    
    @Bean
    @StepScope
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(maxThreads);
        threadPoolTaskExecutor.setThreadGroupName("taskExecutor-batch");
        return threadPoolTaskExecutor;
    }
    
    @Bean(name = "stepToLoadData")
    public Step stepToLoadData() {
        TaskletStep step = stepBuilderFactory.get("stepToLoadData")
                .transactionManager(jpaTransactionManager())
                .<List<FieldSet>, List<myCustomPayloadRecord>>chunk(chunkSize)
                .reader(myCustomFileItemReader(OVERRIDDEN_BY_EXPRESSION))
                .processor(myCustomPayloadRecordItemProcessor())
                .writer(myCustomerWriter())
                .faultTolerant()
                .skipPolicy(new AlwaysSkipItemSkipPolicy())
                .skip(DataValidationException.class)
                .listener(new CustomReaderListener())
                .listener(new CustomProcessListener())
                .listener(new CustomWriteListener())
                .listener(new CustomSkipListener())
                .taskExecutor(taskExecutor())
                .throttleLimit(maxThreads)
                .build();
        step.registerStepExecutionListener(stepExecutionListener());
        step.registerChunkListener(new CustomChunkListener());
        return step;
    }
    
      @Autowired
        @Qualifier("jobToLoadTheData")
        private Job loadTheData;
    
        @Autowired
        private JobLauncher jobLauncher;
    
        @PostConstruct
        public void launchJob () throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException
        {
            JobParameters parameters = (new JobParametersBuilder()).addDate("random", new Date()).toJobParameters();
            jobLauncher.run(loadTheData, parameters);
        }
    
     public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
    }
    
    现在,通常我是从AmazonS3 bucket中读取这个
    .csv
    ,但由于我是在本地测试,我只是将.csv放在项目目录中,并通过触发
    应用程序.java
    主类中的作业直接读取它(如上所示)。另外,我在这个
    BatchConfiguration
    类中定义了一些其他bean,但我不想让这篇文章过于复杂,从我所做的谷歌搜索来看,问题可能在于我发布的方法(希望如此)

    另外,我想指出,与Google/stackoverflow上的一篇文章类似,一个用户也有类似的问题,我创建了一个
    @RestController
    端点,它只调用
    .run()
    方法
    JobLauncher
    ,然后传入
    JobToLoadTheData
    Bean,并触发批插入。你猜怎么着即使在spring>=2.2.1上,数据也能很好地保存到数据库中


    这是怎么回事?这是线索吗?某种类型的实体或事务管理器是否出了问题?我会接受任何建议!我可以提供你们可能需要的更多信息,所以请提问。

    您正在定义一个类型为
    JobRepository
    的bean,并希望它能在Spring Batch中获取。这是不对的。您需要提供一个
    BatchConfigurer
    并覆盖
    getJobRepository
    。下文对此进行了解释:

    这也记录在启用批处理的部分中。因此,在您的情况下,需要定义类型为
    Batchconfigurer
    的bean,并覆盖
    getJobRepository
    getTransactionManager
    ,类似于:

    @Bean
    public BatchConfigurer batchConfigurer(EntityManagerFactory entityManagerFactory, DataSource dataSource) {
        return new DefaultBatchConfigurer(dataSource) {
            @Override
            public PlatformTransactionManager getTransactionManager() {
                return new JpaTransactionManager(entityManagerFactory);
            }
    
            @Override
            public JobRepository getJobRepository() {
                JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
                jobRepositoryFactoryBean.setDataSource(dataSource);
                jobRepositoryFactoryBean.setTransactionManager(getTransactionManager());
                // set other properties
                return jobRepositoryFactoryBean.getObject();
            }
        };
    }
    

    在Spring引导上下文中,如果需要,您还可以覆盖
    org.springframework.Boot.autoconfigure.batch.jpabatchconfigure
    createTransactionManager
    createJobRepository
    方法。

    我在您的帖子中添加了“BatchConfigurer”bean,但这不起作用。然后我删除了它,并让我在上面发布的“BatchConfiguration”类扩展了“DefaultBatchConfigurer”,然后我以这种方式重写了这些方法,但这不起作用。我尝试了不同于PlatformTransactionManager的事务管理器,并在Step vs JobRepository中使用了不同的事务管理器,而且在Spring>=2.2.1上几乎没有其他功能(它在Spring 2.2.0上运行良好)。我对您关于JobRepository没有被我的Spring接受的评论有点困惑,正如我这样定义的,在SpringBootStarter2.2.0上,数据可以很好地保存到数据库中吗?无需定义任何BatchConfigurer匿名类或扩展
    @Bean
    public BatchConfigurer batchConfigurer(EntityManagerFactory entityManagerFactory, DataSource dataSource) {
        return new DefaultBatchConfigurer(dataSource) {
            @Override
            public PlatformTransactionManager getTransactionManager() {
                return new JpaTransactionManager(entityManagerFactory);
            }
    
            @Override
            public JobRepository getJobRepository() {
                JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
                jobRepositoryFactoryBean.setDataSource(dataSource);
                jobRepositoryFactoryBean.setTransactionManager(getTransactionManager());
                // set other properties
                return jobRepositoryFactoryBean.getObject();
            }
        };
    }