KINTO Tech Blog
SpringBatch5

コピペで即完成!Spring Batch (Spring Boot 3)

Cover Image for コピペで即完成!Spring Batch (Spring Boot 3)

目次

  1. はじめに
  2. この記事の対象者
  3. GitHubリポジトリ
  4. スケルトンバッチの解説
  5. DBとCSVのバッチ解説
  6. 継続的インテグレーション
  7. 終わりに

はじめに

こんにちは。KINTOテクノロジーズの共通サービス開発グループ[1][2][3][4][5]のエンジニア、宮下です。

今回、Spring Boot 3を使ったバッチ処理の開発を担当しました。久しぶりのバッチ作成で、基礎を思い出しながら作業を進める中で、Spring Batchのクラス構成やアノテーションに苦労しました。

特に困ったのは、Spring Boot 3に対応した日本語情報が少なかったことです。公式ドキュメントを参考にしながら試行錯誤しましたが、自分の要件に合う設定を見つけられず、マルチデータベース構成やエラー対応でつまずくこともありました。その結果、動かしては修正するという悪循環に陥り、1からバッチを作り上げる大変さを痛感しました。

この記事では、その経験を活かして同じ悩みを抱える方がスムーズにSpring Batchを導入できるように、以下の内容をまとめました。

Spring Boot3 バッチのスケルトン
GitHubからクローンして実行するだけで、すぐに動くバッチが手に入ります。あとは、業務ロジックを追加するだけで、バッチ開発が完了します。余計な設定や準備に悩む必要はありません。

バッチ処理によくあるユースケースの実装例と解説
スケルトンとは別に、以下のマルチデータベース構成を活用したバッチ処理のサンプルコードも紹介します。

  1. DBのレコードをCSV出力するバッチ
  2. CSVの内容をDBに登録するバッチ

この記事を通じて、Spring Batchの導入や開発が少しでも簡単になれば幸いです。

この記事の対象者

以下のような方々を対象にしています:

  • 初めてSpring Batch 5 のフレームワークでバッチを開発をする方。
  • Spring Batchに触れるのが久しぶりで、ジョブやステップなどの基本的な実装方法を思い出す必要がある方。
  • Spring Boot2 Batchのサポート終了で、3へのバージョンアップに伴う新しい構文や設定変更で困っている方。
  • 本当はJavaのmainメソッドで動くシンプルなバッチが好きだが、Springバッチで作る必要が出てきた方。(私もそうです)
  • とにかく手早くバッチの作成タスクを終わらせたい方。

これらに当てはまる方の参考になればと思います。

githubリポジトリ

https://github.com/kinto-technologies/SpringBoot3BatchStarter/
こちらが、私のリポジトリです。クローンとお使いのIDEへの取り込みをお願いします。

GUIでクローンしたい方は、GitHub Desktopの公式アプリを使うと、簡単にリポジトリをクローンできます。

リポジトリのディレクトリ構成

リポジトリは以下のように構成されています。

.
├── gradlew                # Gradleラッパー
├── settings.gradle
├── compose.yaml           # Docker Compose設定ファイル
├── init-scripts           # データベース初期化用SQLスクリプト
│   ├── 1-create-table.sql
│   └── 2-insert-data.sql
├── dbAndCsvBatch          # “DB to CSV” と “CSV to DB” のバッチプロジェクト
│   ├── build.gradle
│   └── src
│       ├── main
│       └── test
└── skeletonBatch          # スケルトンバッチプロジェクト
    ├── build.gradle
    └── src
        ├── main
        └── test

スケルトンバッチの解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/tree/main/skeletonBatch
業務ロジックを追加するだけでバッチが即完成する、必要なコードのみで構成したスケルトンコードです。

JobとStepの基礎

スケルトンに業務ロジックを追加して動かすだけでも良いですが、フレームワークの基礎を理解しておくと、より安心して使えますよね。ここでは、Springバッチの核となるJobStepについて、手短に解説します。

Jobについて

Jobとは、バッチ処理の全体を管理する単位で、1つまたは複数のStepを含みます。これにより、バッチ処理を効率的に実行・管理できます。Spring Batchでは、複数のJobを定義することができ、各Jobを個別に実行したり、連携させて実行することも可能です。

Jobでできること

機能 説明 ユースケース
連携実行 あるジョブの成功後に次のジョブを実行 日次バッチの順次実行
失敗時の実行 ジョブ失敗時にリカバリー処理を実行 エラーログの出力、通知送信
並行実行 複数ジョブの同時実行 独立した処理の効率化
並列実行 同一ジョブの複数インスタンス実行 大量データの分散処理

Step(ステップ)の基礎

Stepとは?

ジョブ内の各処理単位を表します。1つのジョブには複数のステップを登録でき、ステップの動作もジョブと同様に柔軟に設計可能です。

実装方式の選択 - ここがポイント!

Stepの実装にはChunkTaskletの2つの方式があります。

1.チャンク(Chunk)処理

大量データを効率的に処理するための方式です。

特徴

3つのフェーズで処理を分割

  • Reader: データを一定量ずつ読み込み

    • 例: FlatFileItemReaderで、CSVファイルを100行単位で読取
  • Processor: データを1件ずつ加工

    • 例: CustomProcessorで、日付フォーマットの変換
  • Writer: まとめて出力

    • 例: JdbcBatchItemWriterで、DBへの一括INSERT
  • トランザクションの単位は、チャンクサイズ(例:100件)ごとにコミットされます。つまり、途中でエラーになっても、正常終了したチャンクサイズ分はコミットされます。

2.タスクレット(Tasklet) 処理

シンプルな処理を1つ実行する方式です。

特徴

  • シンプルな実装
  • 分かりやすい処理フロー
  • デバッグが容易

実装方式の使い分け

観点 Chunk Tasklet
データ量 大量に適する 少量に適する
処理の複雑さ 複雑な処理に適する シンプルな処理に適する
管理コスト 高め 低め
デバッグ やや複雑 容易
トランザクション チャンク単位 処理単位

まとめ

  • Jobはバッチ処理全体の管理単位
  • Stepは具体的な処理の実行単位
  • Chunkは大量データの効率的な処理に適する
  • Taskletはシンプルな処理に最適
  • 処理内容に応じてChunkとTaskletを適切に使い分けることが重要

YAML設定とSpringバッチの管理テーブル

まず、Spring Batchをバッチ専用モードで動かす基本的な設定について説明します。

YAML設定

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/skeletonBatch/src/main/resources/application.yml

この設定により、Spring Bootアプリケーションはサーバーレスモードで動作します。
通常のSpring BootアプリケーションではTomcatなどのサーバーを起動してプロセスを維持しますが、バッチ処理ではそれが不要です。この設定により、バッチ処理が完了するとプロセスが終了します。


Spring Batch 管理テーブル

Springバッチでは、実行結果を記録するための管理テーブルをDB上に作成する必要があります。でも、DBやテーブルの管理ってちょっと面倒ですよね。そもそもDBが無い環境で動かすこともあるはずです。その場合は、H2のオンメモリDBを使うのがおすすめです。プロセスが終わるとメモリが開放されてDBとテーブルが無くなります
application.ymlにDB設定を書かなければ、この設定が自動で適用されます。
ただし、永続化のメリットとしては、実行結果を保持し、途中で中断した場合でも再開可能な状態を維持できる点が挙げられます。

コード解説JOBクラス

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/skeletonBatch/src/main/java/com/example/batch/job/SampleJob.java#L16-L41

Spring BatchでJobを登録・実行するための中心的なクラスです。

クラス定義のポイント

  1. @Configuration

    • Spring Batchの設定クラスとして認識されます
    • このアノテーションにより、このクラスがSpringの設定を行うクラスだと認識されます
    • Jobを定義するクラスには必須のアノテーションです
  2. @Bean

    • Spring Frameworkで管理したいオブジェクトを生成するメソッドに付けます
    • この場合、createSampleJobメソッドが生成するJobをSpringが管理できるようになります
    • バッチ処理実行時に、このメソッドで作られたJobが使用されます

依存クラスの役割

  • JobRepository: ジョブの実行状態を管理します
  • PlatformTransactionManager: データベースの整合性を保つために使用します
  • SampleLogic: 実際の業務処理を行います

処理の流れ

  1. ジョブ登録開始のログを出力
  2. Stepの作成(処理単位の定義)
  3. Jobの作成とStepの登録
  4. ジョブ登録完了のログを出力

トランザクション管理

  • 正常終了: すべての処理が成功すると、データベースの変更が確定されます
  • 異常終了: エラーが発生すると、データベースの変更が取り消されます

これにより、バッチ処理の信頼性が保証されます。

ポイント

  • @ConfigurationアノテーションでSpring Batchの設定クラスとして認識されます
  • JobBuilderFactoryとStepBuilderFactoryを使用してジョブとステップを構築します
  • Taskletパターンを採用することで、シンプルな実装を実現しています

Logicクラスの解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/skeletonBatch/src/main/java/com/example/batch/logic/SampleLogic.java#L13-L35

このクラスは、バッチの実際の処理内容を定義するものです。

クラス定義のポイント

  1. @Component

    • このクラスをSpringで管理できるようにします
    • これにより、他のクラスで@Autowiredして使用できます
  2. Taskletインターフェース

    • Spring Batchの処理単位を表すインターフェース
    • executeメソッドに実際の処理を実装します

処理の流れ

  1. バッチ処理開始のログを出力
  2. SampleServiceのprocessメソッドを呼び出し
  3. 正常終了時は完了ログを出力
  4. エラー発生時は例外をログ出力して再スロー
  5. バッチ処理終了のログを出力

エラー処理

  • 正常終了: RepeatStatus.FINISHEDを返してバッチを完了
  • 異常終了: 例外をキャッチしてログを出力し、上位に例外を通知

Serviceクラスの解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/skeletonBatch/src/main/java/com/example/batch/service/SampleService.java#L8-L23

このクラスは、実際の業務ロジックを記述するためのクラスです。

クラス定義のポイント

  1. @Service

    • Spring Frameworkのサービスクラスとして認識されます
    • ビジネスロジックを実装するクラスであることを示します
  2. processメソッド

    • バッチで実行したい業務処理を実装するメソッド
    • 処理開始と完了のログ出力を含みます

カスタマイズ方法

以下のような変更が可能です:

  • クラス名の変更
  • メソッド名の変更
  • 引数や戻り値の追加
  • 業務ロジックの実装(例:データ検証、変換処理、外部システム連携)

これで、スケルトンバッチの主要なクラスの解説は終わりです。このように疎結合な設計にすることで、メンテナンスしやすく、テストも容易なバッチを実現できます。

実際にバッチを動かしてみる

IDEから起動する場合

BatchApp クラスを起動してください。通常の Spring Boot アプリケーションと同じ要領で動作します。

ターミナルからGradle経由で起動する場合

以下のコマンドを実行してください。

./gradlew :skeletonBatch:bootRun

ターミナルから実行可能JARファイルを生成して実行する場合

Gradle のデフォルトタスクを実行すると、JAR ファイルが生成されるように設定済みです。以下の手順で実行可能です。

cd skeletonBatch
../gradlew

java -jar build/libs/batch-skeleton*.jar

ログから実際の動きを確認

Spring Batchの実行フローを、出力されるログから順番に確認していきましょう。

1. JOB登録の確認

batch.log
----------- Registering job: sample -----------
----------- Job registered successfully: sample-job -----------

解説

Spring Boot起動時に、sample-jobというバッチジョブが正常に登録されました。


2. バッチ処理の実行

batch.log
Started BatchApp in 0.456 seconds (process running for 0.616)
Running default command line with: []
Job: [SimpleJob: [name=sample-job]] launched with the following parameters: [{}]
Executing step: [processing-step]
----------- START ----------- Batch Processing -----------
--- Starting batch process ---
--- Batch process completed ---
Processing completed successfully
-----------  END  ----------- Batch Processing -----------
Step: [processing-step] executed in 3ms
Job: [SimpleJob: [name=sample-job]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 9ms

解説

ログから確認できる重要なポイント:

  1. sample-jobが実行され、processing-stepというステップが開始
  2. 開始(START)と終了(END)のログにより、処理の境界が明確に解ります。
  3. ステップが正常に完了し、ジョブ全体がCOMPLETEDステータスで終了
  4. アプリケーションが正常に終了

スケルトンバッチの解説まとめ

  • 業務ロジックを追加するだけでバッチが完成
  • 生成されたJARファイルはcronなどのスケジューラと連携可能
  • シンプルな構成で保守性が高い

このスケルトンコードが、皆様のバッチ開発の効率化に貢献できれば幸いです。


DBとCSVのバッチ解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/tree/main/dbAndCsvBatch
こちらのディレクトリです。

概要

このプロジェクトには2つのバッチ処理が含まれています:

  1. DBからCSVを出力

    • データベースからレコードを抽出してCSV形式で出力
    • 起動時引数でデータの抽出条件を変更可能
    • デフォルト設定でもすぐに動作可能
  2. CSVからDBへ登録

    • CSV形式のデータを読み取ってデータベースに一括登録
    • 実行するにはCSVが必要なため、まずは上記の出力バッチを実行してください

ローカルDBのセットアップ

このバッチでは、MySQLのローカルDBを使用します。以下の手順でセットアップしてください。

  1. MySQLコンテナの起動
docker compose up -d
  1. 動作確認 MySQLコンテナに接続してサンプルデータを確認します:
docker exec -it mysql-container mysql -u sampleuser -psamplepassword sampledb

サンプルクエリの実行:

sql
mysql> 
SELECT *
FROM member
WHERE delete_flag = 0
  AND type IN (1, 2, 3)
ORDER BY type ASC;

mysql> exit
Bye

Entityクラスの自動生成

GitHubリポジトリをクローンした直後、Entityクラスが見つからないエラーが発生する場合があります。これはjOOQによるEntityクラスの自動生成が必要なためです。
以下のどちらのコマンドでもEntityクラスが生成されます。

自動生成の実行

# デフォルトタスクの実行
cd dbAndCsvBatch/
../gradlew

# または、generatejOOQタスクを直接実行
../gradlew generateJooq

build.gradleは追加の設定を行わなくても利用できるように工夫しています。デフォルトタスクを実行すると、以下の処理が動くように設定済みです。

  1. ビルド結果のクリーンアップ
  2. Javaコードの整形(Google Java Format)
  3. Entityクラスの自動生成(jOOQプラグイン)
  4. コンパイル
  5. 静的解析とテスト(JUnit、カバレッジ、SpotBugs)
  6. 実行可能JARの生成

生成されたEntityクラス

以下のコマンドで生成されたEntityクラスを確認できます。

tree dbAndCsvBatch/build/generated-src

#treeコマンドをインストールしていない場合はこちら
ls -R dbAndCsvBatch/build/generated-src 
出力例
dbAndCsvBatch/build/generated-src/jooq
└── main
    └── com
        └── example
            └── batch
                └── jooq
                    ├── DefaultCatalog.java
                    ├── Keys.java
                    ├── Sampledb.java
                    ├── Tables.java
                    └── tables
                        ├── Member.java
                        └── records
                            └── MemberRecord.java

クラスが見つからないエラー発生時は

お使いのIDEによっては、Entityクラスが見つからないエラーが発生するかもしれません。
その場合は以下の方法でコンパイルエラーを解消して下さい。

  1. IDEのビルドパスにgenerated-src/jooqを追加
  2. generated-src/jooq配下のクラスをdbAndCsvBatch/src/main/javaにコピー

これらの対応でコンパイルエラーが解消され、プロジェクトが正常にビルドされるようになります。

業務環境でのエンティティ自動生成方法

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/build.gradle#L101-L124

jOOQ gradle plugin website

バッチの解説

長くなってきているので、スケルトンで解説した内容と、Springバッチフレームワークに直接関係ないCSV操作のコード解説は省略し、新しいバッチの設定についてのみ解説したいと思います。

マルチデータベース設定

このバッチでは、2つのデータベースを使用するマルチデータベース構成を採用しています。

  1. H2 (オンメモリデータベース)
  • Spring Batchの管理用テーブルとして利用。オンメモリDBなのでプロセス終了時にリセットされます。
  1. MySQL (業務ロジック用データベース)
  • 業務ロジックで使用。Dockerコンテナ上で動作します。

設定ファイル (application-local.yml)

Spring Bootの設定ファイルでは、spring.datasource 配下に2つのデータベースを設定します。

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/resources/application-local.yml

設定ファイル(yml)には2つのDBが設定されています。また、ファイル名の末尾に local と server のプロファイルを付けています。実行時引数で使用する設定ファイルを切り替えられるようになっています。server 用は、Dockerではなく、皆さんが業務で使用する MySQLサーバーの接続文字列に書き換える想定です。

データソース設定クラスの解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/java/com/example/batch/config/DataSourceConfig.java#L11-L37

2つのデータベースをSpring Batchで使用するための設定クラスです。

アノテーション解説

  1. @Configuration
  • Springの設定クラスであることを示す
  • このクラスで定義したBeanはSpringが管理する
  1. @ConfigurationProperties
  • application.ymlの設定値を取得する
  • 例:spring.datasource.mysqlmainの設定を読み込み
  1. @BatchDataSource
  • Spring Batchの管理テーブル用のデータソースを指定
  • H2データベースに対して使用
  1. @Primary
  • 優先して使用するデータソースを指定
  • MySQLデータソースをデフォルトとして使用

Jobクラス解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/java/com/example/batch/job/DbToCsvJob.java#L31-L39

新機能の追加

  1. 実行番号の自動採番
  .incrementer(new RunIdIncrementer())
  • ジョブの実行番号(run.id)を自動的にインクリメント
  • 実行履歴の管理に使用
  1. リスナーの追加
  .listener(listener)
  • ジョブの開始前と終了後に処理を追加
  • ログ出力やメール通知などに利用可能

Logicクラス解説

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/java/com/example/batch/logic/DbToCsvLogic.java#L18-L49

パラメータ設定

@Value("${batch.types:2,3,4}")
private String typesConfig;
  • application.ymlに設定がない場合は、2,3,4がデフォルト値として使用されます
  • 実行時に--batch.types=1,2のように引数を渡すと、その値が優先されます

エラーハンドリング

contribution.setExitStatus(ExitStatus.FAILED);
  • エラー発生時に明示的に失敗ステータスを設定
  • 正常終了時は自動的にCOMPLETEDとなる

RepositoryクラスとjOOQの解説

アノテーション解説

@Repository

  • Springのコンテナによって自動的に管理されます。
  • SQLException などのデータベース固有の例外を Spring の DataAccessException に変換します。
  • このクラスがデータアクセスに関わるものであることを明示的に示します。これにより、コードの可読性と保守性が向上します。

jOOQとは

jOOQは、SQLをJavaコードとして記述できるライブラリです。

  • SQLの構文をそのままJavaコードで表現
  • テーブル定義からEntityクラスを自動生成
  • タイプセーフなSQL操作を実現

Select処理

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/java/com/example/batch/repository/MemberRepository.java#L18-L25

主な特徴

  1. SQLライクな記述

    • SQL文をJavaコードで直接表現
    • SQLの知識がそのまま活用可能
  2. 自動生成クラス解説

    • MEMBER: テーブル定義を表現(カラム名など)
    • MemberRecord: レコードのマッピング用

バルク(一括)Insert処理

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/java/com/example/batch/repository/MemberRepository.java#L54-L69

バルクInsert
  • 1レコードずつinsertするのではなく、複数のデータを一括で登録します。
  • バッチ処理では、DB負荷軽減のため複数レコードをまとめて処理するのが一般的です。

Repositoryクラスのまとめ

  • jOOQのGradleプラグインを使用することで、データベーススキーマからエンティティを自動生成して使用します。
  • 自動生成されたクラスを使用することで、型安全性を確保しつつ、SQL構文を直感的にJavaで記述できます。
  • テーブル定義の変更があった場合でも、クラスを再生成するだけで対応可能です。

DBからCSVバッチ解説

DBからCSVバッチを起動します。起動方法はスケルトンバッチと同じ要領です。

  • ターミナルからGradle経由で起動する場合

./gradlew :dbAndCsvBatch:bootRun
  • ターミナルから実行可能JARファイルを生成して実行する場合

cd dbAndCsvBatch/
../gradlew
java -jar build/libs/batch-dbAndCsv*.jar 

ここでは、スケルトンバッチの起動方法と同じく、起動引数を付けずにAppクラスを実行しました。

batch.log
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jobLauncherApplicationRunner' defined in class path resource [org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.class]: Job name must be specified in case of multiple jobs
	at com.example.batch.DbAndCsvBatchApp.main(DbAndCsvBatchApp.java:16)

Caused by: java.lang.IllegalArgumentException: Job name must be specified in case of multiple jobs

エラーが発生しました。

なぜエラーが発生したのか?

エラーの原因は、複数のジョブが定義されているため、フレームワークがどのジョブを実行するか判断できなかったことです。そのため、実行するジョブを明示的に指定する必要があります。このプロジェクトでは2つのジョブが定義されています。一方、スケルトンバッチにはジョブが1つしか定義されていないため、このエラーは発生しません。

起動引数を指定して実行する

エラーを解消するには、実行時にジョブ名と環境名を起動引数として指定します。

IDEで実行する場合は、実行時引数設定に次の内容を指定してください。

--spring.batch.job.name=DB_TO_CSV --spring.profiles.active=local
  • --spring.batch.job.name=DB_TO_CSV: 実行するジョブ名を指定します。
  • --spring.profiles.active=local: ローカル環境用の設定プロファイルを有効にします。

実行コマンドの例

# Gradleを使用する場合
./gradlew :dbAndCsvBatch:bootRun --args="--spring.batch.job.name=DB_TO_CSV --spring.profiles.active=local"

# 実行可能JARを直接使用する場合
cd dbAndCsvBatch/
java -jar build/libs/batch-dbAndCsv*.jar --spring.batch.job.name=DB_TO_CSV --spring.profiles.active=local

無事起動したと思うので、ログの内容を確認します。

ログ出力例

batch.log
##### KEY:"sun.java.command", VALUE:"com.example.batch.DbAndCsvBatchApp --spring.batch.job.name=DB_TO_CSV --spring.profiles.active=local"
##### Spring Batch ##### - Job: DB_TO_CSV, Profile: local

起動引数をログ出力するコードを App クラスに仕込んでいます。
ログから、ジョブ名とプロファイル名が正しく渡されていることが確認できました。

ログ出力例

batch.log
----------- JOB [Job Name:DB_TO_CSV] START! -----------
Executing step: [DB_TO_CSV-step]
Fetching members with types = [1, 2, 3]

リスナー (BatchNotificationListener) の beforeJob メソッドのログが出力されています。
また、typesに設定ファイルの値が設定されたことが確認できました。

ログ出力例

batch.log
-> with bind values      : select `sampledb`.`member`.`id`, `sampledb`.`member`.`type`, `sampledb`.`member`.`name`, `sampledb`.`member`.`email`, `sampledb`.`member`.`phone`, `sampledb`.`member`.`address`, `sampledb`.`member`.`delete_flag`, `sampledb`.`member`.`created_at`, `sampledb`.`member`.`updated_at`
from `sampledb`.`member` 
where (`sampledb`.`member`.`delete_flag` = 0 and `sampledb`.`member`.`type` in (1, 2, 3)) order by `sampledb`.`member`.`type`

Version                  : Database version is supported by dialect MYSQL: 9.1.0
Fetched result           : +----+----+----------+----------------------+----------+-----------------------------+-----------+-------------------+-------------------+
                         : |  id|type|name      |email                 |phone     |address                      |delete_flag|created_at         |updated_at         |
                         : +----+----+----------+----------------------+----------+-----------------------------+-----------+-------------------+-------------------+
                         : |   1|   1|John Doe  |john.doe@example.com  |1234567890|123 Main St, City, Country   |          0|2024-12-07T09:46:07|2024-12-07T09:46:07|
                         : |   2|   1|Jane Smith|jane.smith@example.com|0987654321|456 Oak St, Town, Country    |          0|2024-12-07T09:46:07|2024-12-07T09:46:07|
                         : |  26|   1|John Doe  |john.doe@example.com  |1234567890|123 Main St, City, Country   |          0|2024-12-09T05:36:37|2024-12-09T05:36:37|
                         : |  27|   1|Jane Smith|jane.smith@example.com|0987654321|456 Oak St, Town, Country    |          0|2024-12-09T05:36:37|2024-12-09T05:36:37|
                         : |   3|   2|ABC Corp  |contact@abccorp.com   |5678901234|789 Pine St, Village, Country|          0|2024-12-07T09:46:07|2024-12-07T09:46:07|
                         : +----+----+----------+----------------------+----------+-----------------------------+-----------+-------------------+-------------------+
Fetched row(s)           : 5 (or more)
Batch process completed successfully.
Step: [DB_TO_CSV-step] executed in 193ms
Job: [SimpleJob: [name=DB_TO_CSV]] completed with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 212ms
----------- JOB [Job Name:DB_TO_CSV] FINISHED! status:[COMPLETED] -----------

https://github.com/kinto-technologies/SpringBoot3BatchStarter/blob/main/dbAndCsvBatch/src/main/resources/logback.xml#L21-L22

logback.xmlの設定で、jOOQのクエリとその結果をログ出力するように設定しています。実際のクエリーと結果が見られるとデバックしやすいですよね。
また、リスナーの終了ログも出力されており、バッチが正常に終了したことが確認できました。

生成されたCSVファイルの確認

members.csv
"id","type","name","email","phone","address","deleteFlag","createdAt","updatedAt"
"1","1","John Doe","john.doe@example.com","1234567890","123 Main St, City, Country","0","2024-12-11T06:05:26","2024-12-11T06:05:26"
"2","1","Jane Smith","jane.smith@example.com","0987654321","456 Oak St, Town, Country","0","2024-12-11T06:05:26","2024-12-11T06:05:26"
"3","2","ABC Corp","contact@abccorp.com","5678901234","789 Pine St, Village, Country","0","2024-12-11T06:05:26","2024-12-11T06:05:26"
"5","3","Alice Premium","alice.premium@example.com","4561237890","987 Maple St, City, Country","0","2024-12-11T06:05:26","2024-12-11T06:05:26"
"6","3","Charlie Davis","charlie.davis@example.com","1112223333",,"0","2024-12-11T06:05:26","2024-12-11T06:05:26"

CSVファイルが出力された事が確認できました。CSVへの出力と、読み込みにはopencsvのライブラリを使用しています。

opencsv website

実行時引数での types の上書き確認

最後に、このバッチには実行時引数にwhereのin句に設定するtypesを起動引数で渡せる機能がありました。試してみましょう。以下の起動引数を指定することで、where の in 条件に指定する types をカスタマイズできます。

--spring.batch.job.name=DB_TO_CSV --batch.types=4,5 --spring.profiles.active=local

ログ出力例

batch.log
-> with bind values      : select `sampledb`.`member`.`id`, `sampledb`.`member`.`type`, `sampledb`.`member`.`name`, `sampledb`.`member`.`email`, `sampledb`.`member`.`phone`, `sampledb`.`member`.`address`, `sampledb`.`member`.`delete_flag`, `sampledb`.`member`.`created_at`, `sampledb`.`member`.`updated_at` from `sampledb`.`member` where (`sampledb`.`member`.`delete_flag` = 0 and 
`sampledb`.`member`.`type` in (4, 5)) order by `sampledb`.`member`.`type`

設定ファイルの値が、実行時引数で上書きされていることが解ります。

CSVからDBバッチ解説

バッチを起動します。実行時引数でジョブ名を指定して実行します。

--spring.batch.job.name=CSV_TO_DB --spring.profiles.active=local

ログを確認します。

batch.log
insert into `sampledb`.`member` (`name`, `email`, `phone`, `address`, `type`) values 
('Premium Corp', 'premium@corporate.com', '8889997777', '555 High St, City, Country', 4),
('Elite Ltd', 'elite@elitecorp.com', '4445556666', '777 Sky Ave, Town, Country', 4),
('Guest User1', 'guest1@example.com', '', 'Guest Address 1, City, Country', 5),
('Guest User2', 'guest2@example.com', '9998887777', '', 5)

Affected row(s)          : 4

バルク (一括) でinsertされているのが確認できました。

継続的インテグレーション

このプロジェクトでは、GitHub Actionsを使用してCI(継続的インテグレーション)を実現しています。コードの変更がプッシュまたはプルリクエストされるたびに、全ての処理が自動で行われ、品質管理が効率的に進められます。

主な流れ

  1. MySQLのセットアップ: Docker Composeを使ってMySQLを起動し、必要なテーブルを確認。
  2. JDK 21のセットアップ: Java 21をインストールしてビルド環境を整備。
  3. jOOQでのクラス生成: データベーススキーマから自動でエンティティクラスを生成。
  4. ビルドとテスト: Gradleでビルドを行い、JUnitやJacocoやSpotBugsを用いて品質チェックを実施。

コードの変更時、コンパイルエラーやJUnitテストエラーを即座に検出し、迅速に対応できます。
さらに、ビルドとテストが成功すると、GitHubリポジトリのREADMEに以下のようなバッジが表示され、プロジェクトの状態を一目で確認できます:
buildAndTest

コードカバレッジの可視化

このプロジェクトではCodecovを使用してテストカバレッジを測定・可視化しています。プルリクエスト時にカバレッジレポートが自動生成され、以下のバッジでカバレッジ率を確認できます:

codecov

これにより:

  • テストの網羅性を視覚的に把握
  • カバレッジの変更を迅速に検知
  • 品質管理の透明性を向上

カバレッジレポートの詳細はCodecovのダッシュボードで確認できます。

終わりに

いかがでしたでしょうか?

このプロジェクトでは、スケルトンコードを基盤に、Spring Batchを活用したバッチ開発を効率よく進められるよう工夫しています。「DB to CSV」「CSV to DB」といったよくあるユースケースを取り上げ、DB設定やテーブル定義、CSVレイアウトを変更するだけで簡単にカスタマイズできる柔軟性を備えています。

このスケルトンを活用して、皆さんの業務に合わせたロジックを追加し、バッチ開発がスムーズになることを願っています。

この記事が参考になった、コピペでバッチが動いた!という方は、GitHubリポジトリに⭐を付けていただけると嬉しいです。

最後までお読みいただき、ありがとうございました🙇‍♂️

脚注
  1. 共通サービス開発グループメンバーによる投稿 1
    [ グローバル展開も視野に入れた決済プラットフォームにドメイン駆動設計(DDD)を取り入れた ] ↩︎

  2. 共通サービス開発グループメンバーによる投稿 2
    [入社 1 年未満メンバーだけのチームによる新システム開発をリモートモブプログラミングで成功させた話] ↩︎

  3. 共通サービス開発グループメンバーによる投稿 3
    [JIRA と GitHub Actions を活用した複数環境へのデプロイトレーサビリティ向上の取り組み] ↩︎

  4. 共通サービス開発グループメンバーによる投稿 4
    [ VSCode Dev Container を使った開発環境構築 ] ↩︎

  5. 共通サービス開発グループメンバーによる投稿 5
    [ MinIOを用いたS3ローカル開発環境の構築ガイド(AWS SDK for Java 2.x) ] ↩︎

Facebook

関連記事 | Related Posts

We are hiring!

【データエンジニア】分析G/名古屋・大阪

分析グループについてKINTOにおいて開発系部門発足時から設置されているチームであり、それほど経営としても注力しているポジションです。決まっていること、分かっていることの方が少ないぐらいですので、常に「なぜ」を考えながら、未知を楽しめるメンバーが集まっております。

【プロダクト開発バックエンドエンジニア(リーダークラス)】共通サービス開発G/東京・大阪

共通サービス開発グループについてWebサービスやモバイルアプリの開発において、必要となる共通機能=会員プラットフォームや決済プラットフォームなどの企画・開発を手がけるグループです。KINTOの名前が付くサービスやKINTOに関わりのあるサービスを同一のユーザーアカウントに対して提供し、より良いユーザー体験を実現できるよう、様々な共通機能や顧客基盤を構築していくことを目的としています。