如何基于JdbcPagingItemReader实现可动态传参的自定义ItemReader?
解决方案:基于JdbcPagingItemReader实现动态参数注入
要实现从REST API传递的JobParameters动态注入到JdbcPagingItemReader,核心是将Reader的作用域改为StepScoped,让它在Step执行阶段(而非容器启动阶段)初始化,从而能访问到运行时的JobParameters。以下是具体实现步骤:
1. 调整Reader的作用域与参数注入
将原有的@Bean改为@StepScope,并通过@Value注解获取JobParameters中的变量,同时使用参数化查询避免SQL注入:
import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.batch.item.database.JdbcPagingItemReader; import org.springframework.batch.item.database.PagingQueryProvider; import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder; import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class BatchConfig { @Bean @StepScope // 关键:将Reader设为Step作用域,运行时初始化 public JdbcPagingItemReader<Orders> reader( DataSource dataSource, @Value("#{jobParameters['var1']}") String var1, // 注入JobParameters中的var1 @Value("#{jobParameters['var2']}") String var2) { // 注入JobParameters中的var2 // 构建分页查询提供者 SqlPagingQueryProviderFactoryBean queryProviderFactory = new SqlPagingQueryProviderFactoryBean(); queryProviderFactory.setDataSource(dataSource); queryProviderFactory.setSelectClause("select *"); queryProviderFactory.setFromClause("from orders"); // 使用参数占位符,而非硬编码变量 queryProviderFactory.setWhereClause("where order_status = :var1 and date < :var2"); queryProviderFactory.setSortKey("date"); PagingQueryProvider queryProvider = null; try { queryProvider = queryProviderFactory.getObject(); } catch (Exception e) { throw new RuntimeException("Failed to create query provider", e); } // 构建参数源,绑定JobParameters中的变量 Map<String, Object> parameterValues = new HashMap<>(); parameterValues.put("var1", var1); parameterValues.put("var2", var2); // 构建JdbcPagingItemReader return new JdbcPagingItemReaderBuilder<Orders>() .dataSource(dataSource) .queryProvider(queryProvider) .parameterValues(parameterValues) .pageSize(3) .rowMapper(new BeanPropertyRowMapper<>(Orders.class)) .build(); } }
2. 调整Job启动代码,传入所需的所有参数
确保在启动Job时,将var2也传入JobParameters(原代码中仅传入了var1):
public void orderProcess(String var1, String var2) { // 新增var2参数 taskExecutor.execute(() -> { try { JobParameters jobParameters = new JobParametersBuilder() .addString("var1", var1) .addString("var2", var2) // 传入var2参数 .addString("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S").format(Calendar.getInstance().getTime())) .addDate("date", new Date()) .toJobParameters(); jobLauncher.run(job, jobParameters); } catch (Exception e) { LogUtil.logError(LOGGER, "ERROR IN STARTING JOB", e); } }); }
关键说明
- StepScope的作用:默认的
@Bean是单例模式,容器启动时就会初始化Reader,此时JobParameters尚未传入。@StepScope会让Reader在Step执行时才实例化,此时可以获取到当前Job的参数。 - 参数化查询:使用命名参数(
:var1、:var2)而非直接拼接字符串,既避免SQL注入风险,也符合Spring Batch的参数绑定规范。 - 异常处理优化:原代码中的
e.printStackTrace()改为抛出运行时异常,便于Spring Batch捕获并处理Job执行错误。
内容的提问来源于stack exchange,提问作者Ooze
相关产品推荐
相关产品推荐

