Spring Boot 2.2.1+版本Spring Batch调用JPA Repository无法向Postgres存数据
根因
你遇到的问题核心是任务触发时机和Spring Boot 2.2.1+的上下文初始化流程不兼容:
- Spring Boot 2.2.1.RELEASE对Bean初始化顺序、事务AOP代理的加载逻辑做了调整,
@PostConstruct执行时,Spring Data JPA的事务切面、事务管理器的完整初始化还未结束,Spring Batch依赖的部分后置处理器也没完全加载。此时同步启动批量任务,调用JPA Repository时会因为事务上下文创建异常进入死循环挂起,这就是你看到Hikari连接一直占用、代码卡在AOP逻辑里的原因。 - 通过Controller触发任务正常的原因很明确:Controller只有在Spring上下文完全刷新、所有Bean和代理都初始化完成后才会接收请求,此时事务配置完全生效,所以运行没有问题。
解决办法
方案1:用ApplicationRunner/CommandLineRunner触发启动任务(最推荐)
这两个接口的执行时机是Spring上下文完全刷新、所有Bean初始化完成之后,完全适配新版本的启动流程,你只需要把原来@PostConstruct里的逻辑移过去即可:
@SpringBootApplication public class Application { @Autowired @Qualifier("jobToLoadTheData") private Job loadTheData; @Autowired private JobLauncher jobLauncher; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public ApplicationRunner runBatchJob() { return args -> { JobParameters parameters = new JobParametersBuilder() .addDate("random", new Date()) .toJobParameters(); jobLauncher.run(loadTheData, parameters); }; } }
删除你原来写的@PostConstruct启动逻辑即可。
方案2:配置JobLauncher异步执行
如果你一定要在启动阶段触发任务,也可以给JobLauncher配置异步执行器,避免阻塞初始化流程:
@Bean public JobLauncher jobLauncher(JobRepository jobRepository) { SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher(); simpleJobLauncher.setJobRepository(jobRepository); // 新增异步执行器配置 simpleJobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor()); return simpleJobLauncher; }
这样jobLauncher.run()会直接返回,不会卡在初始化阶段,等上下文初始化完成后任务才会实际执行事务相关逻辑。
可选优化
你现在的事务管理器配置没有问题,可补充EntityManagerFactory的绑定,避免潜在的持久化上下文冲突:
@Bean @Primary public JpaTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) { final JpaTransactionManager tm = new JpaTransactionManager(); tm.setDataSource(dataSource); tm.setEntityManagerFactory(entityManagerFactory); return tm; }
内容的提问来源于stack exchange,提问作者PainIsAMaster
相关产品推荐
相关产品推荐

