在Spring中如何在步骤之间传递信息?

9

我想制作一个Spring Batch,但我没有相关经验。

每个批处理步骤之间是否可以传递信息,还是它们必须完全独立?

例如,如果我有:

   <batch:step id="getSQLs" next="runSQLs">
        <batch:tasklet transaction-manager="TransactionManager"
            ref="runGetSQLs" />
    </batch:step>

    <batch:step id="runSQLs">
        <batch:tasklet transaction-manager="TransactionManager"
            ref="runRunSQLs" />
    </batch:step>

当getSQLs触发一个bean时,该bean会执行一个类来生成一个类型为String的列表。是否可以引用该列表以供runSQLs触发的bean使用?(“触发”可能不是正确的术语,但我认为你知道我的意思)

更新: 因此,getSQLs步骤会触发此bean:

<bean id="runGetSQLs" class="myTask"
    scope="step">
    <property name="filePath" value="C:\Users\username\Desktop\sample.txt" />
</bean>

触发myTask类,该类执行此方法:

  @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

    ExecutionContext stepContext = this.stepExecution.getExecutionContext();
    stepContext.put("theListKey", sourceQueries);

    return RepeatStatus.FINISHED;
}

我需要在execute方法中传递stepExecution吗?

3个回答

14

Spring Batch 支持通过 ExecutionContext (更确切地说是 JobExecutionContext) 将数据推送到未来的作业步骤中。在这里,我参考了官方文档中的示例,因为它对我来说是最终的参考:

要使数据可用于未来的步骤,必须在步骤完成后将其“提升”到 Job ExecutionContext 中。

Spring Batch 为此提供了 ExecutionContextPromotionListener

该侦听器应与共享数据的步骤进行配置,如下所示:

<batch:step id="getSQLs" next="runSQLs">
    <batch:tasklet transaction-manager="TransactionManager"
        ref="runGetSQLs" />
    <listeners>
        <listener>
            <beans:bean id="promotionListener" class="org.springframework.batch.core.listener.ExecutionContextPromotionListener">
                <beans:property name="keys" value="theListKey"/>
            </beans:bean>
        </listener>
    </listeners>
</batch:step>

<batch:step id="runSQLs">
    <batch:tasklet transaction-manager="TransactionManager"
        ref="runRunSQLs" />
</batch:step>

你的执行代码块应按以下方式填充数据:

// ...
ExecutionContext stepContext = this.stepExecution.getExecutionContext();
stepContext.put("theListKey", yourList);

然后在接下来的步骤中,可以使用带有@BeforeStep注释的后处理钩子来检索此List,如下所示:

@BeforeStep
public void retrieveSharedData(StepExecution stepExecution) {
    JobExecution jobExecution = stepExecution.getJobExecution();
    ExecutionContext jobContext = jobExecution.getExecutionContext();
    this.myList = jobContext.get("theListKey");
}

我的代码似乎无法识别stepExecution。我已经导入了org.springframework.batch.core.StepExecution。这里我错过了什么? - user2665166
你应该更新帖子并附上你的代码块,同时提及你目前所做的工作。 - tmarwen
已更新。我还更改了步骤以匹配您的示例。 - user2665166
4
最常见的将数据从一个步骤传递到下一个步骤的用例却没有被考虑,却出现了奇怪的将信息升级至任务上下文的解决方案。如果你有一个分区器并且要并行运行步骤,则该解决方案将无效。 - thedoctor
1
@thedoctor,在数据库BATCH_JOB_EXECUTION_CONTEXT.SERIALIZED_CONTEXT中存储中间数据并不是最佳解决方案。需要在作业步骤之间传递数据的要求意味着您违反了批处理的理念)) - gavenkoa
显示剩余3条评论

6

Java配置方式。

步骤1:配置ExecutionContextPromotionListener

@Bean
    public ExecutionContextPromotionListener executionContextPromotionListener()
    {
        ExecutionContextPromotionListener executionContextPromotionListener = new ExecutionContextPromotionListener();
        executionContextPromotionListener.setKeys(new String[] {"MY_KEY"});
        return executionContextPromotionListener;   

    }

步骤二:使用ExecutionContextPromotionListener配置步骤
@Bean
    public Step myStep() {
        return stepBuilderFactory.get("myStep")
                .<POJO, POJO> chunk(1000)
                .reader(reader()                
                .processor(Processor())
                .writer(Writer()
                .listener(promotionListener())
                .build();
    }

第三步:访问处理器中的数据
    @BeforeStep
    public void beforeStep(StepExecution stepExecution) {
         jobExecutionContext = stepExecution.getJobExecution().getExecutionContext();
         jobExecutionContext.getString("MY_KEY")
    }

步骤四:设置处理器中的数据

@BeforeStep
        public void beforeStep(StepExecution stepExecution) {
            stepExecution.getJobExecution().getExecutionContext().put("MY_KEY", My_value);
        }

谢谢,Niraj。这正是我所期待的内容。 - jalpa

4
我建议在使用ExecutionContext传递步骤之间的信息时三思而行。通常情况下,这意味着作业设计不够完善。Spring Batch的主要思想是处理大量数据。ExecutionContext用于存储有关作业/步骤进度的信息,以在发生故障时减少不必要的工作量。按设计,您无法将大数据放入ExectionContext中。完成步骤后,您应该以可靠易读的形式(文件、数据库等)获得信息。这些数据可以用作下一步的输入。对于简单的作业,我建议只使用作业参数作为信息源。
在您的情况下,“runGetSQLs”看起来不是一个好的步骤候选项,但如果您希望,可以将其实现为Spring bean,并在“runRunSQLs”步骤中进行自动装配(这也是一个可以争论的好的步骤候选项)。根据您的命名,runGetSQLs看起来像是ItemReader,而runRunSQLs看起来像是ItemWriter。因此,它们是步骤的一部分,而不是不同的步骤。在这种情况下,您不需要将信息传输到其他步骤中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接