
現代のビジネスアプリケーションにおいて、大量のデータを効率的かつ確実に処理するバッチ処理は不可欠です。
特にJavaエコシステムでは、Spring BootとSpring Batchの組み合わせが、堅牢なバッチアプリケーション開発のデファクトスタンダードとなっています。
本記事では、この強力な組み合わせを活用し、H2データベース上で、CSVファイルからの指示に基づくCRUD操作(登録、更新、削除)を行うバッチ処理の実装を、初心者向けにステップバイステップで解説します。
本記事で学べること
Spring Batchの基本的な概念から、H2データベースのセットアップ、そして実際のCRUD処理の実装、さらにはAPI経由での手動起動やJavaの最新機能の活用まで、実践的な知識を網羅します。
- Spring Bootプロジェクトのセットアップ
- H2データベースの組み込みと設定
- CSVファイルからのデータ読み込み(ItemReader)
- CRUD操作を決定するデータ処理ロジック(ItemProcessor)
- H2データベースへのCRUD操作(ItemWriter)
- Spring Batchジョブの定義と実行
- (応用編 1)バッチ処理をAPIで手動起動する
- (応用編 2)Javaの最新機能(Virtual Threads, Pattern Matching for switch)の活用
このガイドを通じて、Spring BootとSpring Batchを用いたモダンなバッチ開発の基礎を習得し、ご自身のプロジェクトに活かすための第一歩を踏み出しましょう。
対象読者
- Spring Batchの基本的な概念を学びたい方
- Spring Bootでバッチアプリケーションを開発したい方
- H2データベースを使った簡単なデータ操作をバッチで実現したい方
- CSVファイルからの入力に基づいてデータベースを操作するバッチ処理に興味がある方
目次
- 1. Spring BatchとH2データベースの基本
- 1-1. Spring Batchとは
- 1-2. H2データベースとは
- 1-3. 関連記事
- 2. プロジェクトセットアップ
- 2-1. Spring Bootプロジェクトの作成
- 2-2. 必要な依存関係の追加(
pom.xml)
- 3. H2データベースの準備と設定
- 3-1.
application.propertiesの設定 - 3-2. 初期データ(
data.sql)
- 3-1.
- 4. CSVファイルからのCRUD指示の読み込み(ItemReader)
- 4-1. CSVファイルフォーマットの定義
- 4-2. データモデルの作成
- 4-3.
ItemReaderの実装
- 5. データ処理(ItemProcessor)
- 6. H2データベースへのデータ操作(ItemWriter)
- 7. バッチジョブの定義と実行
- 7-1.
BatchConfigクラスでのJob/Step定義 - 7-2. バッチの起動方法
- 7-1.
- 8. 応用編1:バッチ処理をAPIで手動起動する
- 8-1. ジョブ自動実行の無効化
- 8-2. 手動実行用APIエンドポイントの作成
- 8-3. APIの実行方法
- 9. 応用編2:Javaの最新機能の活用
- 9-1. Virtual Threadsの活用
- 9-2. Pattern Matching for switchの活用
- まとめ
1. Spring BatchとH2データベースの基本
1-1. Spring Batchとは
Spring Batchは、堅牢でスケーラブルなバッチアプリケーションを開発するためのSpring Frameworkのサブプロジェクトです。大量のデータを処理する際に必要となる、トランザクション管理、チャンク処理、再起動、スキップ、リトライなどの機能を提供します。
1-2. H2データベースとは
H2データベースは、Javaで書かれたリレーショナルデータベース管理システムです。非常に軽量で高速であり、インメモリモードで動作させることができるため、開発やテスト環境での利用に最適です。本記事では、このインメモリモードを利用して、手軽にデータベース操作を試します。
1-3. 関連記事
Spring Bootバッチ環境構築やバッチ処理の基本構造に関しては、以下の記事で詳細に解説していますので、是非ご覧ください。
2. プロジェクトセットアップ
2-1. Spring Bootプロジェクトの作成
Spring Initializrを使用して、新しいSpring Bootプロジェクトを作成します。
プロジェクトのメタデータは以下のように設定します。
- Project: Maven Project
- Language: Java
- Spring Boot: 最新の安定版(例: 3.5.7)
- Group:
com.example - Artifact:
spring-batch-h2-crud - Name:
spring-batch-h2-crud - Description:
Demo H2 Batch project for Spring Boot(任意) - Package name:
com.example.springbatchh2crud - Packaging: Jar
- Configuration: Properties
- Java: 最新のLTS版(例: 25)または選択可能なバージョン(例: 17, 21)
以下の依存関係(Dependencies)を追加してください。
- Spring Batch: バッチ処理のフレームワーク
- Spring Web: H2コンソールを有効にするため(任意ですが、開発時に便利です)
- H2 Database: インメモリデータベース
- Spring Data JDBC: データベースアクセスを簡素化するため
プロジェクトを生成し、お好みのIDE(IntelliJ IDEA, VS Codeなど)で開いてください。
生成されたプロジェクトは、以下のようなSpring Bootアプリケーションの構造を持ちます。
このフォルダ構造は、本記事の完成形を示しています。
spring-batch-h2-crud
├───pom.xml
└───src
├───main
│ ├───java
│ │ └───com
│ │ └───example
│ │ └───springbatchh2crud
│ │ ├───SpringBatchH2CrudApplication.java
│ │ ├───config
│ │ │ └───BatchConfig.java
│ │ ├───controller
│ │ │ └───JobLaunchController.java
│ │ ├───model
│ │ │ ├───User.java
│ │ │ └───UserOperation.java
│ │ ├───processor
│ │ │ └───UserOperationProcessor.java
│ │ ├───repository
│ │ │ └───UserRepository.java
│ │ └───writer
│ │ └───UserOperationWriter.java
│ └───resources
│ ├───application.properties
│ ├───input
│ │ └───input.csv
│ ├───sql
│ │ └───data.sql
│ ├───static
│ └───templates
└───test
└───java
└───com
└───example
└───springbatchh2crud
└───SpringBatchH2CrudApplicationTests.java2-2. 必要な依存関係の追加(pom.xml)
もし既存のプロジェクトにSpring BatchとH2データベースを追加する場合は、pom.xmlに依存関係を追加します。以下の設定を参考にしてください。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-batch-h2-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-batch-h2-crud</name>
<description>Demo H2 Batch project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>25</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>3. H2データベースの準備と設定
3-1. application.propertiesの設定
src/main/resources/application.propertiesファイルに、H2データベースとH2コンソールの設定を追加します。
spring.application.name=spring-batch-h2-crud
# H2 データベース設定
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# H2コンソール設定 (開発時のみ有効にすることを推奨)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# JPA 設定
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.defer-datasource-initialization=true
# Spring Batch 設定
spring.batch.jdbc.initialize-schema=always
spring.batch.job.enabled=true
spring.sql.init.data-locations=classpath:sql/data.sqlspring.application.name=spring-batch-h2-crud- この設定は、Spring Bootアプリケーションの名前を定義します。アプリケーションのログ出力や監視ツールなどで識別しやすくなります。
- H2 データベース設定
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE- H2データベースへの接続URLを指定します。
jdbc:h2:mem:testdb: インメモリデータベースとしてtestdbという名前のデータベースを作成します。アプリケーションが停止するとデータは失われます。DB_CLOSE_DELAY=-1: 最後の接続が閉じられてもデータベースを閉じないようにします。これにより、複数の接続やアプリケーションのライフサイクルを通じてデータベースが利用可能になります。DB_CLOSE_ON_ON_EXIT=FALSE: JVMが終了してもデータベースを閉じないようにします。インメモリデータベースの場合、これは通常、アプリケーションの再起動時にデータがリセットされることを意味します。
spring.datasource.driverClassName=org.h2.Driver- H2データベースに接続するためのJDBCドライバーのクラス名を指定します。
spring.datasource.username=sa- データベース接続に使用するユーザー名を指定します。H2データベースのデフォルトユーザー名です。
spring.datasource.password=- データベース接続に使用するパスワードを指定します。H2データベースのデフォルトではパスワードは空です。
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect- JPAプロバイダ(ここではHibernate)がH2データベース固有のSQL方言を使用するように設定します。これにより、H2データベースに最適化されたSQLが生成されます。
- JPA 設定
spring.jpa.hibernate.ddl-auto=update- アプリケーション起動時にデータベースのスキーマ(テーブル構造など)を自動的に操作する方法を定義します。
update: エンティティクラスの定義に基づいて、既存のスキーマを更新します。新しいテーブルやカラムが追加されますが、既存のデータは保持されます。開発環境でスキーマ変更を頻繁に行う場合に便利です。
spring.jpa.show-sql=true- Hibernateが実行するSQL文をログに出力するように設定します。デバッグやSQLの確認に役立ちます。
spring.jpa.properties.hibernate.format_sql=trueshow-sqlがtrueの場合に、出力されるSQLを整形して読みやすくします。
spring.jpa.defer-datasource-initialization=true- データソースの初期化(
schema.sqlやdata.sqlの実行)を、JPAのスキーマ生成が完了するまで遅延させます。これにより、JPAがテーブルを作成した後に初期データが投入されるため、ddl-autoとdata.sqlを併用する際の競合を防ぎ、ベストプラクティスとされています。
- データソースの初期化(
- H2コンソール設定 (開発時のみ有効にすることを推奨)
spring.h2.console.enabled=true- H2データベースのWebベースの管理コンソールを有効にします。開発中にデータベースの内容を視覚的に確認するのに非常に便利です。
spring.h2.console.path=/h2-console- H2コンソールにアクセスするためのURLパスを設定します。アプリケーション起動後、
http://localhost:8080/h2-consoleにアクセスしてデータベースの内容を確認できます。
- H2コンソールにアクセスするためのURLパスを設定します。アプリケーション起動後、
- Spring Batch 設定
spring.batch.jdbc.initialize-schema=always- Spring Batchが自身のメタデータ(ジョブの実行履歴などを管理するテーブル)をデータベースに自動的に作成・初期化する方法を定義します。
always: アプリケーション起動時に常にメタデータスキーマを作成します。
spring.batch.job.enabled=true- Spring Bootアプリケーション起動時に、定義されているSpring Batchジョブの自動実行を有効にします。
- この値を
falseに設定することにより、APIエンドポイント経由など、プログラムから明示的にジョブを実行する制御が可能になります。
spring.sql.init.data-locations=classpath:sql/data.sql- データベースの初期化に使用するデータスクリプトの場所を指定します。
classpath:sql/data.sql:src/main/resources/sql/data.sqlファイルが初期データとして使用されることを示します。
これらの設定により、H2データベースをバックエンドとして使用し、Spring Batchジョブを柔軟に制御できるSpring Bootアプリケーションの基盤が構築されます。
3-2. スキーマ定義と初期データ(data.sql)
src/main/resources/sqlディレクトリにdata.sqlを作成し、データベースのスキーマ定義と初期データを設定します。
data.sql
INSERT INTO users (name, email, status) VALUES ('Alice', 'alice@example.com', 'ACTIVE');
INSERT INTO users (name, email, status) VALUES ('Bob', 'bob@example.com', 'INACTIVE');4. CSVファイルからのCRUD指示の読み込み(ItemReader)
4-1. CSVファイルフォーマットの定義
CSVファイルは、各行が1つの操作(登録、更新、削除)を表し、その操作に必要なデータを含みます。
フォーマット例:operation,id,name,email
operation:INSERT,UPDATE,DELETEのいずれかid: ユーザーID(UPDATE,DELETE操作で必須)name: ユーザー名(INSERT,UPDATE操作で任意)email: メールアドレス(INSERT,UPDATE操作で任意)
input.csvの例
src/main/resources/inputディレクトリにinput.csvを作成します。
CREATE,,Charlie,charlie@example.com,ACTIVE
UPDATE,1,Alice_Updated,alice_updated@example.com,ACTIVE
DELETE,2,,,4-2. データモデルの作成
CSVの各行をマッピングするためのJavaクラスを作成します。ここでは、Lombokのアノテーションを使用して、ボイラープレートコード(getter, setter, toStringなど)を削減します。
src/main/java/com/example/springbatchh2crud/model/UserOperation.java
package com.example.springbatchh2crud.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserOperation {
private String operation;
private Long id;
private String name;
private String email;
private String status;
}CSVの各行をデータベース操作するためのJavaクラスを作成します。ここでは、Lombokのアノテーションを使用して、ボイラープレートコード(getter, setter, toStringなど)を削減します。
package com.example.springbatchh2crud.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String status;
}補足:JPAエンティティの命名とテーブル名の衝突回避について
データベースの予約語とJPAエンティティ名が衝突する場合があります。
例えば、Userというエンティティ名がデータベースの予約語と重なるケースです。
このような場合、Javaのクラス名自体を変更するのではなく、@Table(name = "...") アノテーションを使用してデータベース上のテーブル名のみを変更することがベストプラクティスとされています。
これは、ビジネスロジックを表現するドメインモデル(Javaクラス)と、データを永続化するための永続化モデル(データベーステーブル)の関心を分離し、ドメイン駆動設計の原則に従うためです。
クラス名をビジネスドメインに忠実なものに保ちつつ、データベース固有の制約はアノテーションで吸収することで、コードの可読性と保守性を高めることができます。
4-3. ItemReaderの実装
CSVファイルを読み込み、UserOperationオブジェクトにマッピングするFlatFileItemReaderを設定します。
src/main/java/com/example/springbatchh2crud/config/BatchConfig.javaに以下のBean定義を追加します。
package com.example.springbatchh2crud.config;
import com.example.springbatchh2crud.model.UserOperation;
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.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.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class BatchConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
public BatchConfig(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
) {
this.jobRepository = jobRepository;
this.transactionManager = transactionManager;
}
@Bean
public FlatFileItemReader<UserOperation> userOperationReader() {
return new FlatFileItemReaderBuilder<UserOperation>()
.name("userOperationReader")
.resource(new ClassPathResource("input/input.csv")) // input.csvファイルを指定
.delimited()
.names("operation", "id", "name", "email", "status") // CSVヘッダーに対応するフィールド名
.fieldSetMapper(
new BeanWrapperFieldSetMapper<>() {
{
setTargetType(UserOperation.class);
}
}
)
.build();
}
// ItemProcessor, ItemWriter, Step, Jobの定義は後述
}5. データ処理(ItemProcessor)
ItemProcessorでは、UserOperationオブジェクトを受け取り、そのoperationフィールドに基づいて、後続のItemWriterで実行すべきデータベース操作を決定します。
ここでは、UserOperationオブジェクトをそのまま次のステップに渡しますが、必要に応じて別のオブジェクトに変換することも可能です。
src/main/java/com/example/springbatchh2crud/processor/UserOperationProcessor.java
package com.example.springbatchh2crud.processor;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
import com.example.springbatchh2crud.model.UserOperation;
@Component
public class UserOperationProcessor implements ItemProcessor<UserOperation, UserOperation> {
@Override
public UserOperation process(UserOperation item) throws Exception {
// ここで必要に応じてデータを加工したり、バリデーションを行ったりできます。
// 今回は、UserOperationオブジェクトをそのまま次のステップに渡します。
System.out.println("Processing user operation: " + item);
return item;
}
}BatchConfig.javaにUserOperationProcessorのBean定義を追加します。
// BatchConfig.java (抜粋)
// ...
import com.example.springbatchh2crud.processor.UserOperationProcessor;
// ...
@Configuration
public class BatchConfig {
// ...
@Bean
public UserOperationProcessor userOperationProcessor() {
return new UserOperationProcessor();
}
// ...
}6. H2データベースへのデータ操作(ItemWriter)
ItemWriterでは、UserOperationオブジェクトのoperationフィールドに基づいて、H2データベースに対して適切なSQL操作(INSERT, UPDATE, DELETE)を実行します。
まずはUserRepositoryを実装し、JPAを使って必要なデータアクセスを実装します。
package com.example.springbatchh2crud.repository;
import com.example.springbatchh2crud.model.User;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByEmail(String email);
}補足:Spring Data JPAによる自動実装UserRepositoryインターフェースはJpaRepositoryを継承しているため、Spring Data JPAが自動的に基本的なCRUD(Create, Read, Update, Delete)操作の実装を提供します。
これにより、開発者はデータベースアクセスのためのボイラープレートコードを記述することなく、save(),findById(),findAll(),delete()などのメソッドをすぐに利用できます。
また、findByEmail()のように、命名規則に従うことでカスタムクエリメソッドも自動生成されます。
次にH2データベースに対して適切なSQL操作(INSERT, UPDATE, DELETE)を実装します。
ここでは先ほど実装したUserRepositoryを使用してデータアクセスを実装します。
src/main/java/com/example/springbatchh2crud/writer/UserOperationWriter.java
package com.example.springbatchh2crud.writer;
import com.example.springbatchh2crud.model.User;
import com.example.springbatchh2crud.model.UserOperation;
import com.example.springbatchh2crud.repository.UserRepository;
import java.util.Optional;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
@Component
public class UserOperationWriter implements ItemWriter<UserOperation> {
private final UserRepository userRepository;
public UserOperationWriter(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void write(Chunk<? extends UserOperation> chunk) throws Exception {
for (UserOperation item : chunk) {
switch (item.getOperation()) {
case "CREATE":
// emailが重複しないようにチェック
if (userRepository.findByEmail(item.getEmail()).isEmpty()) {
User newUser = new User(
null,
item.getName(),
item.getEmail(),
item.getStatus()
);
userRepository.save(newUser);
System.out.println("Created User: " + newUser);
} else {
System.out.println(
"Skipped CREATE for existing email: " +
item.getEmail()
);
}
break;
case "UPDATE":
if (item.getId() != null) {
Optional<User> existingUser = userRepository.findById(
item.getId()
);
if (existingUser.isPresent()) {
User userToUpdate = existingUser.get();
userToUpdate.setName(item.getName());
userToUpdate.setEmail(item.getEmail());
userToUpdate.setStatus(item.getStatus());
userRepository.save(userToUpdate);
System.out.println("Updated User: " + userToUpdate);
} else {
System.out.println(
"Skipped UPDATE for non-existent ID: " +
item.getId()
);
}
} else {
System.out.println(
"Skipped UPDATE: ID is null for operation " +
item.getOperation()
);
}
break;
case "DELETE":
if (item.getId() != null) {
userRepository.deleteById(item.getId());
System.out.println(
"Deleted User with ID: " + item.getId()
);
} else {
System.out.println(
"Skipped DELETE: ID is null for operation " +
item.getOperation()
);
}
break;
default:
System.out.println(
"Unknown operation: " + item.getOperation()
);
break;
}
}
}
}BatchConfig.javaにUserOperationWriterのBean定義を追加します。
// BatchConfig.java (抜粋)
// ...
import com.example.springbatchh2crud.writer.UserOperationWriter;
import javax.sql.DataSource;
// ...
@Configuration
public class BatchConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
private final UserRepository userRepository;
public BatchConfig(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
UserRepository userRepository
) {
this.jobRepository = jobRepository;
this.transactionManager = transactionManager;
this.userRepository = userRepository;
}
// ...
@Bean
public UserOperationWriter userOperationWriter() {
return new UserOperationWriter(userRepository);
}
// ...
}7. バッチジョブの定義と実行
7-1. BatchConfigクラスでのJob/Step定義
BatchConfig.javaに、ItemReader、ItemProcessor、ItemWriterを組み合わせてStepとJobを定義します。
完成したBatchConfig.javaのコードを以下に示します。
package com.example.springbatchh2crud.config;
import com.example.springbatchh2crud.model.UserOperation;
import com.example.springbatchh2crud.processor.UserOperationProcessor;
import com.example.springbatchh2crud.repository.UserRepository;
import com.example.springbatchh2crud.writer.UserOperationWriter;
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.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class BatchConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
private final UserRepository userRepository;
public BatchConfig(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
UserRepository userRepository
) {
this.jobRepository = jobRepository;
this.transactionManager = transactionManager;
this.userRepository = userRepository;
}
@Bean
public FlatFileItemReader<UserOperation> userOperationReader() {
return new FlatFileItemReaderBuilder<UserOperation>()
.name("userOperationReader")
.resource(new ClassPathResource("input/input.csv")) // input.csvファイルを指定
.delimited()
.names("operation", "id", "name", "email", "status") // CSVヘッダーに対応するフィールド名
.fieldSetMapper(
new BeanWrapperFieldSetMapper<>() {
{
setTargetType(UserOperation.class);
}
}
)
.build();
}
// ItemProcessor, ItemWriter, Step, Jobの定義は後述
@Bean
public UserOperationProcessor userOperationProcessor() {
return new UserOperationProcessor();
}
@Bean
public UserOperationWriter userOperationWriter() {
return new UserOperationWriter(userRepository);
}
@Bean
public Step processUserOperationStep() {
return new StepBuilder("processUserOperationStep", jobRepository)
.<UserOperation, UserOperation>chunk(10, transactionManager)
.reader(userOperationReader())
.processor(userOperationProcessor())
.writer(userOperationWriter())
.build();
}
@Bean
public Job userOperationJob() {
return new JobBuilder("userOperationJob", jobRepository)
.start(processUserOperationStep())
.build();
}
}7-2. バッチの起動方法
コマンドラインからの実行
このプロジェクトはMaven Wrapper (mvnw) を使用しているため、Mavenを別途インストールする必要はありません。
- macOS / Linux の場合: コマンドの先頭に
./mvnwを付けて実行します。 - Windows の場合: コマンドの先頭に
mvnw.cmdを付けて実行します。
1. パッケージング(実行可能JARの作成)
アプリケーションを実行可能な単一のJARファイルとしてパッケージングします。
./mvnw clean package成功すると target/ ディレクトリ内に spring-batch-h2-crud-0.0.1-SNAPSHOT.jar というファイルが生成されます。
2. アプリケーションの実行
パッケージ化されたJARファイルを使ってアプリケーションを起動します。
java -jar target/spring-batch-h2-crud-0.0.1-SNAPSHOT.jarバッチ処理の実行結果確認
アプリケーションを起動すると、Spring Batchが自動的にuserOperationJobを実行します。
実行後、H2コンソール(http://localhost:8080/h2-console)にアクセスし、usersテーブルの内容を確認してください。CSVファイルで指示したCRUD操作が反映されているはずです。
SELECT * FROM USERS;| ID | NAME | STATUS | |
|---|---|---|---|
| 1 | alice_updated@example.com | Alice_Updated | ACTIVE |
| 3 | charlie@example.com | Charlie | ACTIV |
H2コンソールの利用方法
アプリケーション起動後、H2コンソールを利用してデータベースの内容を確認できます。
- H2コンソールへのアクセス:
ブラウザでhttp://localhost:8080/h2-consoleにアクセスします。 - 正しいJDBC URLでの接続:
H2コンソールのログイン画面で、以下の情報を入力して「接続」ボタンをクリックします。- JDBC URL:
jdbc:h2:mem:testdb(application.propertiesで設定したURLと一致させる必要があります) - ユーザー名:
sa(application.propertiesで設定したユーザー名と一致させます) - パスワード: (空欄、
application.propertiesで設定したパスワードと一致させます)
usersテーブルの内容を確認できます。CSVファイルで指示したCRUD操作が反映されているはずです。 - JDBC URL:
8. 応用編1:バッチ処理をAPIで手動起動する
バッチジョブをAPI経由で手動で起動できるようにすることで、アプリケーションの起動とは独立してジョブの実行を制御できます。
これは、特定のタイミングでジョブを実行したい場合や、外部システムからジョブをトリガーしたい場合に有用です。
8-1. ジョブ自動実行の無効化
まず、アプリケーション起動時にSpring Batchジョブが自動的に実行されないように設定を変更します。src/main/resources/application.propertiesファイルに以下の設定を追加または変更します。
spring.batch.job.enabled=falseこの設定により、Spring Bootアプリケーションが起動しても、JobLauncherApplicationRunnerによるジョブの自動実行は行われなくなります。
8-2. 手動実行用APIエンドポイントの作成
次に、HTTPリクエストを受け付けてバッチジョブを起動するAPIエンドポイントを作成します。src/main/java/com/example/springbatchh2crud/controller/JobLaunchController.javaを新規作成し、以下のコードを記述します。
package com.example.springbatchh2crud.controller;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* [新規] このコントローラは、Spring Batchジョブを手動でトリガーするためのAPIエンドポイントを提供します。
* ジョブを起動時に自動的に実行する代わりに、オンデマンドで実行できるように作成されています。
*/
@RestController
public class JobLaunchController {
private final JobLauncher jobLauncher;
private final Job userOperationJob;
/**
* 依存性注入のためのコンストラクタ。
* @param jobLauncher Spring BatchのJobLauncher。
* @param userOperationJob 起動する特定のジョブ。"userOperationJob"というBean名で識別されます。
*/
@Autowired
public JobLaunchController(
JobLauncher jobLauncher,
@Qualifier("userOperationJob") Job userOperationJob
) {
this.jobLauncher = jobLauncher;
this.userOperationJob = userOperationJob;
}
/**
* /launch-jobにPOSTリクエストが行われたときに'userOperationJob'を起動します。
* <p>
* ジョブを複数回実行できるようにするため、各実行のJobParametersに
* 一意の'time'パラメータが追加されます。Spring Batchは、まったく同じパラメータで
* ジョブが複数回実行されるのを防ぎます。
* </p>
* @return ジョブの起動試行の結果を示すResponseEntity。
*/
@PostMapping("/launch-job")
public ResponseEntity<String> launchJob() {
try {
// ジョブインスタンスが常に一意であることを保証するために、JobParametersにタイムスタンプを追加します
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(userOperationJob, jobParameters);
return ResponseEntity.ok(
"ジョブ '" +
userOperationJob.getName() +
"' は正常に起動されました。"
);
} catch (Exception e) {
return ResponseEntity.internalServerError().body(
"ジョブの起動に失敗しました: " + e.getMessage()
);
}
}
}JobLaunchControllerの解説
@RestController: このクラスがRESTful APIのエンドポイントであることを示します。JobLauncher: Spring Batchが提供するインターフェースで、ジョブを起動するために使用します。Job userOperationJob:BatchConfigで定義したuserOperationJobをインジェクションしています。@Qualifier("userOperationJob")アノテーションは、複数のJobBeanが存在する場合に、どのジョブをインジェクションするかを明示的に指定するために使用します。@PostMapping("/launch-job"):/launch-jobへのPOSTリクエストがこのlaunchJob()メソッドにマッピングされます。JobParameters: Spring Batchでは、同じJobParametersを持つジョブは一度しか実行されません。そのため、System.currentTimeMillis()を使って現在時刻をtimeパラメータとして追加することで、毎回異なるJobParametersを生成し、ジョブの複数回実行を可能にしています。jobLauncher.run(userOperationJob, jobParameters): 実際にジョブを起動する処理です。
8-3. APIの実行方法
アプリケーションを起動した後、任意のHTTPクライアント(例: curl, Postman, VS CodeのREST Clientなど)を使用して、以下のエンドポイントにPOSTリクエストを送信することで、バッチジョブを手動で起動できます。
curl -X POST http://localhost:8080/launch-jobリクエストが成功すると、ジョブ 'userOperationJob' は正常に起動されました。のようなレスポンスが返されます。ジョブの実行状況はアプリケーションのログで確認できます。
curlコマンドの具体的な利用方法については、以下の記事で詳細に解説していますので、是非ご覧ください。
9. 応用編2:Javaの最新機能の活用
9-1. Virtual Threadsの活用
Java 21で正式導入されたVirtual Threads(仮想スレッド)は、軽量なスレッドであり、I/Oバウンドな処理が多いバッチアプリケーションにおいて、スループットの向上に貢献する可能性があります。Spring Batch 5.1以降では、Virtual Threadsを簡単に統合できるようになっています。
BatchConfig.javaに以下の設定を追加することで、Spring Batchのタスク実行にVirtual Threadsを使用できます。
// BatchConfig.java (抜粋)
// ...
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.VirtualThreadTaskExecutor;
// ...
@Configuration
public class BatchConfig {
// ...
@Bean
public TaskExecutor taskExecutor() {
return new VirtualThreadTaskExecutor();
}
@Bean
public Step processUserOperationStep() {
return new StepBuilder("processUserOperationStep", jobRepository)
.<UserOperation, UserOperation>chunk(10, transactionManager)
.reader(userOperationReader())
.processor(userOperationProcessor())
.writer(userOperationWriter())
.taskExecutor(taskExecutor()) // Virtual Threadsを使用するように設定
.build();
}
// ...
}これにより、processUserOperationStep内のチャンク処理がVirtual Threads上で実行されるようになります。特に、データベースアクセスなどのI/O処理が多い場合に、スレッドプールの枯渇を気にすることなく、より多くの並行処理を効率的に実行できるようになります。
9-2. Pattern Matching for switchの活用
Java 17で正式導入されたPattern Matching for switchは、switch文をより簡潔かつ安全に記述するための機能です。UserOperationWriterのwriteメソッド内でoperationフィールドの文字列比較を行っていますが、これをよりモダンなJavaの書き方に変更できます。
UserOperationWriter.javaのwriteメソッド内のswitch文を以下のように変更できます。
// UserOperationWriter.java (抜粋)
// ...
@Override
public void write(Chunk<? extends UserOperation> chunk) throws Exception {
List<UserOperation> inserts = new ArrayList<>();
List<UserOperation> updates = new ArrayList<>();
List<UserOperation> deletes = new ArrayList<>();
for (UserOperation item : chunk.getItems()) {
switch (item.getOperation()) {
case "INSERT" -> inserts.add(item);
case "UPDATE" -> updates.add(item);
case "DELETE" -> deletes.add(item);
default -> System.err.println("Unknown operation: " + item.getOperation());
}
}
// ... 後続の処理は同じ
}
// ...この変更により、switch文の可読性が向上し、より簡潔に各ケースの処理を記述できるようになります。
まとめ
本記事では、Spring BatchとSpring Boot、H2データベースを使用して、CSVファイルからの指示に基づいてデータベースのCRUD操作を行うバッチ処理を実装する方法を解説しました。また、Javaの最新機能であるVirtual ThreadsとPattern Matching for switchを応用することで、よりモダンで効率的なバッチアプリケーションを構築できることを示しました。
このチュートリアルを参考に、ご自身のプロジェクトでSpring Batchを活用してみてください。
免責事項:
本記事は、執筆時点での情報に基づいており、将来的に情報が変更される可能性があります。本記事の内容によって生じたいかなる損害についても、筆者は一切の責任を負いません。読者の皆様ご自身の判断と責任において、本記事の情報をご活用ください。
本記事はチュートリアルを目的としており、本番環境での利用には追加の考慮事項(エラーハンドリング、ロギング、監視、セキュリティなど)が必要です
