简体   繁体   English

Spring批处理在tasklet中执行动态生成的步骤

[英]Spring batch execute dynamically generated steps in a tasklet

I have a spring batch job that does the following... 我有一个春季批处理工作,执行以下操作...

Step 1. Creates a list of objects that need to be processed 步骤1.创建需要处理的对象列表

Step 2. Creates a list of steps depending on how many items are in the list of objects created in step 1. 步骤2.根据步骤1中创建的对象列表中的项目数创建步骤列表。

Step 3. Tries to executes the steps from the list of steps created in step 2. 步骤3.尝试执行步骤2中创建的步骤列表中的步骤。

The executing x steps is done below in executeDynamicStepsTasklet(). 执行x步骤在executeDynamicStepsTasklet()中完成。 While the code runs without any errors it does not seem to be doing anything. 虽然代码运行没有任何错误,但它似乎没有做任何事情。 Does what I have in that method look correct? 我在该方法中看到的内容是否正确?

thanks 谢谢

/* * */ / * * * /

@Configuration
public class ExportMasterListCsvJobConfig {

public static final String JOB_NAME = "exportMasterListCsv";
@Autowired
public JobBuilderFactory jobBuilderFactory;

@Autowired
public StepBuilderFactory stepBuilderFactory;

@Value("${exportMasterListCsv.generateMasterListRows.chunkSize}") 
public int chunkSize;

@Value("${exportMasterListCsv.generateMasterListRows.masterListSql}") 
public String masterListSql;

@Autowired
public DataSource onlineStagingDb;

@Value("${out.dir}") 
public String outDir;

@Value("${exportMasterListCsv.generatePromoStartDateEndDateGroupings.promoStartDateEndDateSql}") 
private String promoStartDateEndDateSql;


private List<DivisionIdPromoCompStartDtEndDtGrouping> divisionIdPromoCompStartDtEndDtGrouping;

private List<Step> dynamicSteps = Collections.synchronizedList(new ArrayList<Step>()) ;


@Bean
public Job exportMasterListCsvJob(
        @Qualifier("createJobDatesStep") Step createJobDatesStep,
        @Qualifier("createDynamicStepsStep") Step createDynamicStepsStep,
        @Qualifier("executeDynamicStepsStep") Step executeDynamicStepsStep) {

    return jobBuilderFactory.get(JOB_NAME)
            .flow(createJobDatesStep)
            .next(createDynamicStepsStep)
            .next(executeDynamicStepsStep)
            .end().build();
}   


@Bean
public Step executeDynamicStepsStep(
        @Qualifier("executeDynamicStepsTasklet")  Tasklet executeDynamicStepsTasklet) {

    return  stepBuilderFactory
                .get("executeDynamicStepsStep")
                .tasklet(executeDynamicStepsTasklet)
                .build();               
}

@Bean
public Tasklet executeDynamicStepsTasklet() {

    return new Tasklet() {

        @Override
        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
            FlowStep flowStep = new FlowStep(createParallelFlow());
            SimpleJobBuilder jobBuilder = jobBuilderFactory.get("myNewJob").start(flowStep);
            return RepeatStatus.FINISHED;
        }
    };
}

public Flow createParallelFlow() {
    SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
    taskExecutor.setConcurrencyLimit(1); 

    List<Flow> flows = dynamicSteps.stream()
            .map(step -> new FlowBuilder<Flow>("flow_" + step.getName()).start(step).build())
            .collect(Collectors.toList());

    return new FlowBuilder<SimpleFlow>("parallelStepsFlow")
            .split(taskExecutor)
            .add(flows.toArray(new Flow[flows.size()]))
            .build();
}

@Bean

public Step createDynamicStepsStep(
        @Qualifier("createDynamicStepsTasklet")  Tasklet createDynamicStepsTasklet) {

    return  stepBuilderFactory
                .get("createDynamicStepsStep")
                .tasklet(createDynamicStepsTasklet)
                .build();               
}   

@Bean
@JobScope
public Tasklet createDynamicStepsTasklet() {

    return new Tasklet() {

        @Override
        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
            for (DivisionIdPromoCompStartDtEndDtGrouping grp: divisionIdPromoCompStartDtEndDtGrouping){

                System.err.println("grp: " + grp);

                String stepName = "stp_" + grp;

                String fileName = grp + FlatFileConstants.EXTENSION_CSV;

                Step dynamicStep = 
                        stepBuilderFactory.get(stepName)
                        .<MasterList,MasterList> chunk(10)
                        .reader(queryStagingDbReader(
                                grp.getDivisionId(), 
                                grp.getRpmPromoCompDetailStartDate(), 
                                grp.getRpmPromoCompDetailEndDate()))
                        .writer(masterListFileWriter(fileName))                                
                        .build(); 

                dynamicSteps.add(dynamicStep);

            } 
            System.err.println("createDynamicStepsTasklet dynamicSteps: " + dynamicSteps);
            return RepeatStatus.FINISHED;
        }
    };
}


public FlatFileItemWriter<MasterList> masterListFileWriter(String fileName) {
    FlatFileItemWriter<MasterList> writer = new FlatFileItemWriter<>();
    writer.setResource(new FileSystemResource(new File(outDir, fileName )));
    writer.setHeaderCallback(masterListFlatFileHeaderCallback());
    writer.setLineAggregator(masterListFormatterLineAggregator());
    return writer;
}

So now I have a list of dynamic steps that need to be executed and I believe that they are in StepScope. 所以现在我有一个需要执行的动态步骤列表,我相信它们在StepScope中。 Can someone advise me on how to execute them 有人可以告诉我如何执行它们

This will not work. 这不行。 Your Tasklet just creates a job with a FlowStep as first Step. 您的Tasklet只是创建一个FlowStep作为第一步的作业。 Using the jobBuilderfactory just creates the job. 使用jobBuilderfactory只创建作业。 it does not launch it. 它没有启动它。 The methodname "start" may be misleading, since this only defines the first step. 方法名“start”可能会产生误导,因为这只定义了第一步。 But it does not launch the job. 但它没有启动这项工作。

You cannot change the structure of a job (its steps and substeps) once it is started. 一旦启动,您就无法更改作业的结构(其步骤和子步骤)。 Therefore, it is not possible to configure a flowstep in step 2 based on things that are calculated in step 1. (of course you could do some hacking deeper inside the springbatch structure and directly modify the beans and so ... but you don't want to do that). 因此,不可能根据步骤1中计算的内容在步骤2中配置流程步骤。(当然,您可以在弹簧结构内部进行更深入的黑客攻击并直接修改bean等等......但是你不要我想这样做)。

I suggest, that you use a kind of "SetupBean" with an appropriate postConstruct method which is injected into your class that configures your job. 我建议你使用一种“SetupBean”和一个适当的postConstruct方法,该方法被注入你的类来配置你的工作。 This "SetupBean" is responsible to calculate the list of objects being processed. 这个“SetupBean”负责计算正在处理的对象列表。

@Component
public class SetUpBean {

  private List<Object> myObjects;

  @PostConstruct
  public afterPropertiesSet() {
    myObjects = ...;
  }

  public List<Object> getMyObjects() {
   return myObjects;
  }
}

@Configuration
public class JobConfiguration {

   @Autowired
   private JobBuilderFactory jobBuilderFactory;

   @Autowired
   private StepBuilderFactory stepBuilderFactory;

   @Autowired
   private SetUpBean setup;

   ... 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM