🦇

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

2024/03/28に公開

目的

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

背景

「Spring Batch入門ガイド」のサンプルプログラムにあるBatchConfigurationクラスは、step1のリーダーとライターの実装を含んでおり、プロセッサーはPersonItemProcessorクラスにあります。
ステップ数が増えた場合、ステップごとにクラスを分けた方が管理しやすくなります。また、チャンク志向のステップでは、リーダー、プロセッサー、ライターを一つのクラスにまとめた方が理解しやすくなります。

要件

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

  1. step1のリーダー、プロセッサー、ライターを一つのクラスにまとめる
  2. BatchConfigurationクラスを設定クラスにするため簡素化する

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

1. リーダー、プロセッサー、ライターを一つのクラスにまとめる

BatchConfigurationクラスをコピーし、クラス名をStep1Configurationに変更します。
importUserJobstep1メソッドは削除します。

Step1Configuration.java
@Configuration
public class Step1Configuration {
    @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 JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
        return new JdbcBatchItemWriterBuilder<Person>()
            .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
            .dataSource(dataSource)
            .beanMapped()
            .build();
    }
}

ただし、PersonItemProcessorクラスはそのままとします。

2. 設定クラスにするため簡素化する

BatchConfigurationクラスは、readerprocessorwriterメソッドは削除します。

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

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

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

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

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

└── src
    └── main
        └── java
            └── com.example.batchprocessing
                ├── BatchConfiguration.java(更新)
                └── Step1Configuration.java(追加)

参考

今回の要件は、単純にクラス分けをすれば実現できましたが、それに気づくまでは、ItemReaderItemProcessorItemWriterをインターフェースしたクラスを実装することで実現していました。

以前の方法

1. リーダー、プロセッサー、ライターを一つのクラスにまとめる

既存のPersonItemProcessorクラスは、プロセッサーのみを定義していますが、このクラスにリーダーとライターの定義も加えて、step1の実装を集約します。
まず、クラス名をPersonItemProcessorからPersonItemHandlerに変更します。
その上で、ItemReaderItemWriterをインターフェースします。

PersonItemHandler.java
public class PersonItemHandler implements ItemProcessor<Person, Person>, ItemReader<Person>, ItemWriter<Person> {

リーダーとライターの実装のために、readerwriterフィールドを追加します。

PersonItemHandler.java
private final FlatFileItemReader<Person> reader;
private final JdbcBatchItemWriter<Person> writer;

また、コンストラクタを追加し、リーダーとライターの初期化をします。

PersonItemHandler.java
public PersonItemHandler(DataSource dataSource) {
    this.reader = new FlatFileItemReaderBuilder<Person>()
            .name("personItemReader")
            .resource(new ClassPathResource("sample-data.csv"))
            .delimited()
            .names("firstName", "lastName")
            .targetType(Person.class)
            .build();
    reader.open(new ExecutionContext());

    this.writer = new JdbcBatchItemWriterBuilder<Person>()
            .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
            .dataSource(dataSource)
            .beanMapped()
            .build();
    writer.afterPropertiesSet();
}

FlatFileItemReaderBuilderJdbcBatchItemWriterBuilder文は、BatchConfigurationクラスにあるコードと同様ですが、それぞれ、以下を追加します。

PersonItemHandler.java
reader.open(new ExecutionContext());
PersonItemHandler.java
writer.afterPropertiesSet();

最後に、インターフェースの実装です。

PersonItemHandler.java
@Override
public void write(Chunk<? extends Person> chunk) throws Exception {
    writer.write(chunk);
}

@Override
public Person read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
    return reader.read();
}

2. 設定クラスにするため簡素化する

BatchConfigurationクラスから、readerprocessorwriterメソッドは削除します。
次に、1.で作成したPersonItemHandler@Beanアノテーションを使って定義します。

BatchConfiguration.java
@Bean
public PersonItemHandler personItemHandler(DataSource dataSource) {
    return new PersonItemHandler(dataSource);
}

最後に、step1メソッドでpersonItemHandlerを使用するように変更します。

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

以上で、すべての要件を満たしました。

まとめ

1. リーダー、プロセッサー、ライターを一つのクラスにまとめる

PersonItemHandlerクラスでは、Spring BatchのItemReaderItemProcessor、およびItemWriterインターフェイスを実装し、これらの機能を一つのクラスに統合しています。これにより、データの読み込み、処理、書き込みのロジックが一箇所に集約され、ステップの管理が容易になります。

2. 設定クラスにするため簡素化する

BatchConfigurationクラスの役割を、ジョブおよびステップの定義に限定します。これにより、具体的なビジネスロジック(読み込み、処理、書き込み)はPersonItemHandlerに委ねられ、BatchConfigurationは設定と構成に集中できるようになります。

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

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

└── src
    └── main
        └── java
            └── com.example.batchprocessing
                ├── BatchConfiguration.java(更新)
                ├── PersonItemHandler.java(追加)
                └── PersonItemProcessor.java(削除)

最後に

このような構成は、一般的なSpring Batchアプリケーションの設計パターンとは異なる可能性がありますこと、ご了承ください。

コード

https://github.com/k0buchi/gs-batch-processing-complete2

Discussion