🦇

「Spring Batch 入門ガイド」を実用化する3

2024/04/17に公開

目的

Spring Batch入門ガイド(バッチサービスの作成)」を基に、追加要件を組み込むことで、より実用的なバッチプログラムの作成方法を解説します。

背景

Step間で、データの引き渡しが必要になる場面がありました。

要件

背景を踏まえて、以下の要件を設定しました。

  1. step1のデータを引き渡す
  2. step2を追加し、引き渡されたデータをログ出力する

これらの要件を満たすために、「Spring Batch入門ガイド」のサンプルプログラムを修正および追加します。

1. step1のデータを引き渡す

1.1 PersonItemWriterクラスの作成

まず、BatchConfigurationからライターをクラス分けし、ItemWriterStepExecutionListenerをインタフェースしたPersonItemWriterクラスを作成します。

PersonItemWriter.java
public class PersonItemWriter implements ItemWriter<Person>, StepExecutionListener   {
    private ListItemWriter<Person> writer = new ListItemWriter<>();
	
    @Override
    public void write(Chunk<? extends Person> chunk) throws Exception {
        writer.write(chunk);
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        ExecutionContext context = stepExecution.getJobExecution().getExecutionContext();
        List<? extends Person> person = (List<? extends Person>) writer.getWrittenItems();
        context.put("listItem", person);
        return stepExecution.getExitStatus();
    }
}

ライターは、ListItemWriterを使用して、Listに値を追加するように変更します。さらに、リスナーを追加し、afterStepでJobExecutionContextに格納します。

1.2 Personクラスのシリアライズ

PersonオブジェクトをJobExecutionContextに格納するには、シリアライズする必要がありますので、Serializableをインタフェースします。

Person.java
public record Person(String firstName, String lastName) implements Serializable {

}

2. step2を追加し、引き渡されたデータをログ出力する

2.1 Step2Taskletクラスの追加

Step2Taskletクラスで、JobExecutionContextに格納されたPersonオブジェクトを取得し、ログ出力します。

Step2Tasklet.java
public class Step2Tasklet implements Tasklet {
    private static final Logger log = LoggerFactory.getLogger(Step2Tasklet.class);

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        ExecutionContext context = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
        @SuppressWarnings("unchecked")
        List<Person> listItem = (List<Person>) context.get("listItem");
        listItem.forEach(person -> log.info("Found <{{}}> in the JobExecution.", person));
        return RepeatStatus.FINISHED;
    }
}

2.2 BatchConfigurationの修正

writerimportUserJobstep1メソッドの修正と、step2メソッドの追加をします。

BatchConfiguration.java
@Configuration
public class BatchConfiguration {
    @Bean
    public FlatFileItemReader<Person> reader() {
        return new FlatFileItemReaderBuilder<Person>()
            .name("personItemReader")
            .resource(new ClassPathResource("sample-data.csv"))
            .delimited()
            .names("firstName", "lastName")
            .targetType(Person.class)
            .build();
    }

    @Bean
    public PersonItemProcessor processor() {
        return new PersonItemProcessor();
    }

    @Bean
    public PersonItemWriter writer() {
        return new PersonItemWriter();
    }

    @Bean
    public Job importUserJob(JobRepository jobRepository,Step step1, Step step2, JobCompletionNotificationListener listener) {
        return new JobBuilder("importUserJob", jobRepository)
            .listener(listener)
            .start(step1)
            .next(step2)
            .build();
    }

    @Bean
    public Step step1(JobRepository jobRepository, DataSourceTransactionManager transactionManager,
                      FlatFileItemReader<Person> reader, PersonItemProcessor processor, PersonItemWriter writer) {
        return new StepBuilder("step1", jobRepository)
            .<Person, Person> chunk(3, transactionManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }

    @Bean
    public Step step2(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder("step2", jobRepository)
            .tasklet(new Step2Tasklet(), transactionManager)
            .build();
    }
}

以上で、要件を満たしました。

参考

「Spring Batch入門ガイド」のサンプルプログラはインメモリデータベースを使用しているため、簡単に内容を確認したい場合は、ログ出力になります。
JobCompletionNotificationListenerクラスでは、peopleテーブルの内容をログ出力していますが、本修正によってpeopleテーブルへの挿入は行っていませんので、ログ出力されません。その代わりに、使用しているテーブル一覧をログ出力するように変更しました。

JobCompletionNotificationListener.java
jdbcTemplate.queryForList(
        "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'"
    ).forEach(result -> log.info("Record <{{}}>", result));

更新・追加ソースファイル

「Spring Batch入門ガイド」のサンプルプログラムに加えた、更新および追加のファイルは以下の通りです。

└── src
    └── main
        └── java
            └── com.example.batchprocessing
                ├── BatchConfiguration.java(更新)
                ├── JobCompletionNotificationListener.java(更新)
                ├── Person.java(更新)
                ├── PersonItemWriter.java(追加)
                └── Step2Tasklet.java(追加)

コード

https://github.com/k0buchi/gs-batch-processing-complete3
Java17、Spring Batch 5.1.0、Spring Boot 3.2.0で、動作確認しています。ビルドツールは、Mavenを使用しています。

関連

「Spring Batch 入門ガイド」を実用化する
「Spring Batch 入門ガイド」を実用化する2

Discussion