Search code examples
springspring-batch

Upgrade spring batch from 4.3.x to 5.0.x


Currently I a working on migrating Spring Boot 2.7.x (Spring Batch 4.3.x) to Spring Boot 3.0.x (Spring Batch 5.0.x) with Mongo DB based Job repository

@Configuration
public class MainBatchConfigurer implements BatchConfigurer {

    @Autowired
    private ExecutionContextDao mongoExecutionContextDao;

    @Autowired
    private JobExecutionDao mongoJobExecutionDao;

    @Autowired
    private JobInstanceDao mongoJobInstanceDao;

    @Autowired
    private StepExecutionDao mongoStepExecutionDao;

    @Override
    public JobRepository getJobRepository() {
        return new SimpleJobRepository(mongoJobInstanceDao, mongoJobExecutionDao, mongoStepExecutionDao, mongoExecutionContextDao);
    }

    @Override
    public PlatformTransactionManager getTransactionManager() {
        return new ResourcelessTransactionManager();
    }

    @Override
    public SimpleJobLauncher getJobLauncher() throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(getJobRepository());
        jobLauncher.afterPropertiesSet();
        return jobLauncher;
    }

    @Override
    public JobExplorer getJobExplorer() {
        return new SimpleJobExplorer(mongoJobInstanceDao, mongoJobExecutionDao, mongoStepExecutionDao, mongoExecutionContextDao);
    }

}

Since 'BatchConfigurer' interface has been removed, I am planning to change 'MainBatchConfigurer' like below

    @Configuration
public class MainBatchConfigurer  {

    @Autowired
    private ExecutionContextDao mongoExecutionContextDao;

    @Autowired
    private JobExecutionDao mongoJobExecutionDao;

    @Autowired
    private JobInstanceDao mongoJobInstanceDao;

    @Autowired
    private StepExecutionDao mongoStepExecutionDao;

    @Bean
    public JobRepository jobRepository() {
        return new SimpleJobRepository(mongoJobInstanceDao, mongoJobExecutionDao, mongoStepExecutionDao, mongoExecutionContextDao);
    }

    @Bean
    public PlatformTransactionManager getTransactionManager() {
        return new ResourcelessTransactionManager();
    }


    public TaskExecutorJobLauncher getJobLauncher() throws Exception {
        TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
        jobLauncher.setJobRepository(jobRepository());
        jobLauncher.afterPropertiesSet();
        return jobLauncher;
    }

    @Bean
    public JobExplorer getJobExplorer() {
        return new SimpleJobExplorer(mongoJobInstanceDao, mongoJobExecutionDao, mongoStepExecutionDao, mongoExecutionContextDao);
    }

}

Previously I have been invoking the job

SimpleJobLauncher jobLauncher = batchConfigurer.getJobLauncher();
JobExecution jobExecution = jobLauncher.run(xxx, jobParameters);

Also with Batch 5.0.x I need to pass the Jobrepository and TransationManager when I create a step and also pass JobRepository when creating Job. I have lots of jobs and steps, do I need to pass in all of these. Is there any way to avoid not setting Jobrepository and TransationManager since I am setting this at "TaskExecutorJobLauncher".

I have multiple jobs defined in separate class and each job class will have its own steps and jobs defined like below. Here is my sample Master,workersteps and jobs defined

 @Bean
    public Step departmentMigrationStep() throws UnexpectedInputException, ParseException {
        return stepBuilderFactory.get("departmentMigratioStep")
                .<departments, departments>chunk(10000)
                .reader(departmentPeekingreader())
                .processor(departmentprocessor())
                .writer(departmentwriter)
                .listener(genericItemReadListener)
                .listener(genericSkipListener)
                .listener(genericStepExecutionListener)
                .listener(genericChunkListener)
                .build();
    }

    @Bean
    public Step departmentValidationStep() throws UnexpectedInputException, ParseException {
        return stepBuilderFactory.get("departmentValidationStep")
                .<xxx, yyy>chunk(10000)
                .reader(departmentPeekingreader)
                .processor(departmentValidationProcessor())
                .writer(departmentValidationWriter)
                .listener(genericChunkListener)
                .build();
    }


    @Bean
    public Step deparmentMasterStep() throws UnexpectedInputException, ParseException {
        return stepBuilderFactory.get("deparmentMasterStep")
                .partitioner("workerStep",departmentPartitioner())
                .step(departmentMigrationStep())
                .taskExecutor(departmentExecutor())
                .gridSize(20)
                .build();

    }

    @Bean
    public Step departmentValidationMasterStep() throws UnexpectedInputException, ParseException{

        return stepBuilderFactory.get("departmentValidationMasterStep")
                .partitioner("workerStep",departmentPartitioner())
                .step(departmentValidationStep())
                .taskExecutor(departmentExecutor())
                .gridSize(20)
                .build();

    }


    @Primary
    @Bean
    public Job processDepartmentMigration() {
        return jobBuilderFactory.get("processDepartmentMigration")
                .incrementer(new RunIdIncrementer()).listener(departmentJobListener())
                .start(deparmentMasterStep())
                .build();
    }

    @Bean
    public Job processdepartmentValidation() {
        return jobBuilderFactory.get("processdepartmentValidation")
                .incrementer(new RunIdIncrementer()).listener(departmentJobListener())
                .start(departmentValidationMasterStep())
                .build();
    }

Solution

  • I have multiple job classes and each job class will have its own Master, worker steps and jobs defined in it

    You can create a base class in which you define a method that returns a step builder on which the job repository is set:

    class BaseConfiguration {
    
       // define common configuration
    
       protected StepBuilder getStep(String name) {
          return new StepBuilder(name, jobRepository());
       }
    
    }
    

    Then extend this class for each job and call the method that creates steps as follows:

    class Job1Config extends BaseConfiguration {
    
       //..
    
        @Bean
        public Step departmentMigrationStep() {
            return super.getStep("departmentMigratioStep")
                    // ...
                    .build();
        }
    }
    

    Note that the transaction manager is not always required, like in the partitioned step. So it is up to you to adapt the method in the base class or create different methtods to return a TaskletStepBuilder, or a PartitionStepBuilder, etc. For example:

    protected <I, O> SimpleStepBuilder<I, O> getChunkedStep(String name, int chunkSize) {
        return new StepBuilder(name, jobRepository())
                .chunk(chunkSize, transactionManager());
    }
    

    This allows you to achieve the same result as with the deprecated JobBuilderFactory and StepBuilderFactory.