概要
業務でSpring Batchが必要となったので勉強したことをまとめてみました
全て小文字に変換して表示するコードも書いてます
Spring Batchとは
Spring Frameworkを中心としたSpringプロジェクトの一つ。
バッチ処理を実現するために使用するフレームワーク。
バッチ処理:あらかじめ一連の処理手順を登録しておき、自動的に処理を行う方式のこと
Spring Batchでできること
- バッチ処理の定期的なコミット
- 並列、直列処理
- 実行タイミングの指定
- 処理結果による後続処理の分岐指定(リトライなど)
構成
構成 | 説明 |
---|---|
JobLauncher | Jobを起動するためのモジュール |
Job | バッチアプリケーションの一連の処理をまとめた1実行単位、Stepを呼び出す |
Step | Jobを構成する処理の単位、1つのJobに対して1~n個Stepを持たせることが可能 |
Tasklet | 実際に実装される場所、①Chunkモデル ②Taskletモデル 2種の実装パターンがある |
JobRepository | 実行履歴を管理する機能、DB上のテーブルで管理され、リトライ時などに使用される |
実装
種類
①Chunkモデル
一定件数のデータごとにまとめて、入力(ItemReader)・加工(ItemProcessor)・出力(ItemWriter)の順で処理する方式
②Taskletモデル
自由に処理を記述できる方式
SQLを1回発行するだけ、コマンドを発行するだけ、といった簡素なケースで使用
また複数のデータベースやファイルにアクセスしながら処理するような複雑で定型化しにくいケースでも使用
① Chunkモデル構成
インターフェース | 説明 |
---|---|
ItemReader | データの入力を担うインターフェース、データベースやファイルからJavaオブジェクトへ変換する |
ItemProcessor | データの加工を担うインターフェース、入力チェックやビジネスロジックを実装する |
ItemWriter | データの出力を担うインターフェース、Javaオブジェクトからデータベースやファイルへ変換する |
処理全体の流れ
やってみた
ゴール
{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
をInputとして、全て小文字に変換して表示する
実行構成
1. mainクラスの作成
@EnableBatchProcessing
アノテーションを付与とあるが、Spring Boot v3.0 以降、このアノテーションは不要になったので削除
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchSampleChunkApplication {
public static void main(String[] args) {
SpringApplication.run(BatchSampleChunkApplication.class, args);
}
}
ItemProcessorの作成
package com.example.demo.chunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@StepScope
@Slf4j
public class SampleProcessor implements ItemProcessor<String, String> {
Logger logger = LoggerFactory.getLogger(SampleProcessor.class);
@Override
public String process(String item) throws Exception {
// 文字列の加工(小文字に変換)
item = item.toLowerCase();
logger.info("Processor:{}", item);
return item;
}
}
2. ItemReaderの作成
package com.example.demo.chunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@StepScope
@Slf4j
public class SampleReader implements ItemReader<String> {
Logger logger = LoggerFactory.getLogger(SampleReader.class);
// 配列
private String[] input = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
private int index = 0;
@Override
public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
if (input.length > index) {
// 配列の文字列を取得
String message = input[index++];
logger.info("Read:{}", message);
return message;
}
return null;
}
}
3. ItemWriterの作成
package com.example.demo.chunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@StepScope
@Slf4j
public class SampleWritter implements ItemWriter<String> {
Logger logger = LoggerFactory.getLogger(SampleProcessor.class);
@Override
public void write(Chunk<? extends String> chunk) throws Exception {
logger.info("writer:{}", chunk);
logger.info("=========");
}
}
4. Batch設定クラスの作成
package com.example.demo.config;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@Configuration
public class BatchConfig {
@Autowired
private ItemReader<String> reader;
@Autowired
private ItemProcessor<String, String> processor;
@Autowired
private ItemWriter<String> writer;
// Jobを生成
@Bean
public Job importUserJob(JobRepository jobRepository, Step chunkStep) {
return new JobBuilder("importUserJob", jobRepository) // Builderの取得
.incrementer(new RunIdIncrementer()) // IDのインクリメント
.start(chunkStep) // 最初のStep
.build(); // Jobの生成
}
@Bean
public Step chunkStep(JobRepository jobRepository, DataSourceTransactionManager transactionManager) {
return new StepBuilder("chunkStep", jobRepository) // Builderの取得
.<String, String>chunk(3, transactionManager)// チャンクの設定
.reader(reader) // readerセット
.processor(processor) // processorセット
.writer(writer) // writerセット
.build(); // Stepの生成
}
}
5. 実行
結果
データが読まれて、小文字に変換していることがわかる2024-02-08T16:12:04.443+09:00 INFO 34882 --- [ restartedMain] c.e.demo.BatchSampleChunkApplication : Started BatchSampleChunkApplication in 1.862 seconds (process running for 7.53)
2024-02-08T16:12:04.446+09:00 INFO 34882 --- [ restartedMain] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2024-02-08T16:12:04.496+09:00 INFO 34882 --- [ restartedMain] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=importUserJob]] launched with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}]
2024-02-08T16:12:04.517+09:00 INFO 34882 --- [ restartedMain] o.s.batch.core.job.SimpleStepHandler : Executing step: [chunkStep]
2024-02-08T16:12:04.535+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Sunday
2024-02-08T16:12:04.537+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Monday
2024-02-08T16:12:04.537+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Tuesday
2024-02-08T16:12:04.538+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:sunday
2024-02-08T16:12:04.538+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:monday
2024-02-08T16:12:04.538+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:tuesday
2024-02-08T16:12:04.538+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : writer:[items=[sunday, monday, tuesday], skips=[]]
2024-02-08T16:12:04.540+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : =========
2024-02-08T16:12:04.543+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Wednesday
2024-02-08T16:12:04.543+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Thursday
2024-02-08T16:12:04.543+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Friday
2024-02-08T16:12:04.543+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:wednesday
2024-02-08T16:12:04.543+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:thursday
2024-02-08T16:12:04.544+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:friday
2024-02-08T16:12:04.544+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : writer:[items=[wednesday, thursday, friday], skips=[]]
2024-02-08T16:12:04.544+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : =========
2024-02-08T16:12:04.545+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleReader : Read:Saturday
2024-02-08T16:12:04.545+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : Processor:saturday
2024-02-08T16:12:04.545+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : writer:[items=[saturday], skips=[]]
2024-02-08T16:12:04.546+09:00 INFO 34882 --- [ restartedMain] com.example.demo.chunk.SampleProcessor : =========
2024-02-08T16:12:04.549+09:00 INFO 34882 --- [ restartedMain] o.s.batch.core.step.AbstractStep : Step: [chunkStep] executed in 31ms
参考資料
Spring Batchのアーキテクチャ
Spring Batch入門
[公式ドキュメント] Creating a Batch Service
勉強になったYoutube
[公式ドキュメント] クラス JobBuilderFactory