如何在Spring Batch中向RepositoryItemReader传递运行时原生查询参数
解决Spring Batch执行周期内固定传入transaction-datetime查询失败事件的问题
问题核心
当前代码直接在Reader中使用OffsetDateTime.now()作为查询参数,会导致每个批次查询时生成新的时间戳,无法满足同一执行周期内所有批次共用同一个transaction-datetime的需求。同时原Repository查询存在语法错误(countQuery中有多余的and)。
解决方案步骤
1. 修正Repository查询方法
先修复countQuery的语法错误,确保查询逻辑正确:
@Query(value = "SELECT * FROM eventsdata et where et.created_on < ?1 and et.status = 'fail'", countQuery = "SELECT count(*) FROM eventsdata et where et.created_on < ?1 and et.status = 'fail'", nativeQuery = true) Page<Event> findEventsByStatus(OffsetDateTime transactionDateTime, Pageable pageable);
2. 基于StepScope实现周期内参数复用
通过@StepScope让Reader在Step启动时初始化,从Job上下文或参数中获取固定的transaction-datetime,确保整个Job周期内参数一致。
方式一:通过Job参数传入时间
在Reader方法上添加@StepScope,并注入Job参数:
@StepScope public RepositoryItemReader<EventData> defineReader(@Value("#{jobParameters['transactionDateTime']}") OffsetDateTime transactionDateTime) { Map<String, Sort.Direction> sortMap = new HashMap<>(); sortMap.put("id", Sort.Direction.ASC); return new RepositoryItemReaderBuilder<EventData>() .repository(eventsDataRepository) .methodName("findEventsByStatus") .arguments(Arrays.asList(transactionDateTime)) .saveState(false) .sorts(sortMap) .pageSize(3) .build(); }
启动Job时传入参数:
JobParameters jobParameters = new JobParametersBuilder() .addOffsetDateTime("transactionDateTime", OffsetDateTime.now()) .toJobParameters(); jobLauncher.run(yourJob, jobParameters);
方式二:通过Job监听器自动生成时间
如果不想手动传入参数,可借助JobExecutionListener在Job启动时生成固定时间并存入上下文:
@Component public class TransactionDateTimeListener implements JobExecutionListener { @Override public void beforeJob(JobExecution jobExecution) { // Job启动时生成时间,存入执行上下文 OffsetDateTime transactionDateTime = OffsetDateTime.now(); jobExecution.getExecutionContext().put("transactionDateTime", transactionDateTime); } @Override public void afterJob(JobExecution jobExecution) { // 可选:执行后的清理操作 } }
修改Reader从Job上下文获取时间:
@StepScope public RepositoryItemReader<EventData> defineReader(@Value("#{jobExecutionContext['transactionDateTime']}") OffsetDateTime transactionDateTime) { // 同方式一的Reader构建逻辑 }
记得在Job配置中注册监听器:
@Bean public Job yourJob(JobBuilderFactory jobBuilderFactory, Step yourStep, TransactionDateTimeListener listener) { return jobBuilderFactory.get("yourJob") .listener(listener) .flow(yourStep) .end() .build(); }
效果说明
两种方式都能保证:
- 同一Job执行周期内,所有批次的查询都会使用同一个transaction-datetime
- 每次Job启动时生成新的时间戳,满足不同执行周期的参数独立性
内容的提问来源于stack exchange,提问作者Bhanupriya Sharma
相关产品推荐
相关产品推荐

