同一Spring Batch作业步骤间ItemReader数据共享方法(Java)
Spring Batch 跨步骤ItemReader数据共享可行方案
针对第一步查询出ID集合、后续步骤复用ID做二次查询的场景,有3种经过生产验证的实现方案,可根据业务数据量、作业可靠性要求选型:
方案1:基于Job级别ExecutionContext存储(原生支持,适合小数据量+需重启容错场景)
Spring Batch 原生提供ExecutionContext作为作业/步骤的共享数据存储,步骤级上下文默认仅步骤内可见,将数据存入作业级上下文即可实现跨步骤访问。
- 实现逻辑:
- 第一步不需要做复杂业务处理,配置一个简单的ItemWriter,把reader读到的所有ID逐条攒到内存集合里
- 给第一步绑定
StepExecutionListener,在afterStep回调(也就是第一步完全执行完、所有ID都收集齐的节点),把攒好的ID集合存入Job级别的ExecutionContext - 后续步骤的ItemReader在初始化阶段(可以用
@BeforeStep回调触发),从Job上下文中取出ID集合,作为查询参数传入自己的数据库查询逻辑
- 示例代码:
// 第一步的ID收集监听器 public class IdCollectListener implements StepExecutionListener { private final List<Long> collectedIds = new ArrayList<>(); // 提供方法给第一步的writer调用,传入单条读到的ID public void addId(Long id) { collectedIds.add(id); } @Override public ExitStatus afterStep(StepExecution stepExecution) { // 存入作业级上下文,设置固定key方便后续步骤读取 stepExecution.getJobExecution() .getExecutionContext() .put("FIRST_STEP_QUERY_IDS", collectedIds); return ExitStatus.COMPLETED; } }
- 注意事项:
- 存入上下文的对象必须实现
Serializable序列化接口,否则作业持久化、重启时会抛序列化异常 - 单份共享数据大小不要超过10KB,ID数量控制在千级以内——因为ExecutionContext的内容会持久化到Spring Batch元数据表中,数据过大会拖慢作业启动、重启速度,甚至撑爆元表字段。
- 存入上下文的对象必须实现
方案2:基于中间临时表存储(适合大数据量场景)
如果第一步查出来的ID是万级甚至十万级以上,用上下文存储会有性能问题,优先选数据库临时表方案。
- 实现逻辑:
- 提前建一张中间临时表,字段至少包含
job_execution_id(作业执行实例ID,做数据隔离)、biz_id(第一步查出来的业务ID) - 第一步配置批量Writer,把读到的ID关联当前作业的
jobExecutionId,批量插入到临时表 - 后续步骤的ItemReader直接关联临时表写SQL,传入当前作业的
jobExecutionId作为查询条件,就能拉到对应ID关联的业务数据 - 整个作业执行完成后,绑定Job监听器删除当前
jobExecutionId对应的临时表数据,避免垃圾数据堆积;如果用的是数据库会话级临时表,连接断开后数据会自动清理,不需要手动删除
- 提前建一张中间临时表,字段至少包含
- 优势:没有数据量上限,不占用JVM内存,也不会给Spring Batch元表造成压力,多作业并行执行时靠
job_execution_id做隔离,不会出现数据串扰。
方案3:基于@JobScope托管Bean存储(适合简单无高可用要求场景)
如果是开发临时作业、或者不需要支持断点重启的内部作业,可以用Spring Bean做共享存储,开发效率最高。
- 实现逻辑:
- 定义一个作业级作用域的Bean,专门存跨步骤共享的数据,必须加
@JobScope注解,保证每个作业执行实例有独立的Bean实例,避免多作业并行时数据覆盖
@JobScope @Component public class JobShareContext { private List<Long> firstStepIds; public List<Long> getFirstStepIds() { return firstStepIds; } public void setFirstStepIds(List<Long> firstStepIds) { this.firstStepIds = firstStepIds; } }- 第一步的Writer收集完所有ID后,直接调用Bean的set方法存入集合
- 后续步骤的Reader直接注入这个Bean,取出ID集合拼查询条件即可
- 定义一个作业级作用域的Bean,专门存跨步骤共享的数据,必须加
- 注意事项:
- 绝对不要用默认单例作用域的Bean存共享数据,多作业并行时一定会出现数据错乱
- 数据存在JVM内存中,作业异常中断重启后内存数据会丢失,不适合对可靠性要求高的生产核心作业。
选型参考
- 千级以内ID、作业需要支持断点重启:选方案1,原生能力无额外依赖
- 万级以上ID、生产核心作业:选方案2,性能稳定无瓶颈
- 临时作业、快速开发、无重启要求:选方案3,代码量最少
避坑提示:不要在ItemReader读数据的过程中就往共享存储写数据、同时让后续步骤提前读取,Spring Batch的步骤生命周期是独立初始化的,后续步骤启动时必须保证第一步已经完成所有ID的收集,否则会出现数据不全、空指针的问题。
内容的提问来源于stack exchange,提问作者rishisai1010
相关产品推荐
相关产品推荐

