Kato83 さんの Web サイト「Pulog プログラムのブログ 略してプログ」の中の「Spring BootでSpring BatchのChunkモデルを試す」というエントリーを参考に Spring Batch を勉強させてもらっているんですが、この記事が書かれたのが約 6年前ということで対象バージョンが古い。今は SpringBatch も 6 までバージョンが上がっていて、そのままソースを拝借したのでは動きません。
そこで、Spring Batch 6 に対応した修正を行ったので、ここで公開しておきます。
俺以上に Spring Batch 素人で、それでも「とりあえず動かしてみんとようわからん」という方向けです(笑)
プロジェクトの依存関係(Dependencies)とかは Kato83 さんのサイトを参考にしてください。
ちなみに、今回は com.netandfield.test フォルダの下に全ファイル配置しています。
(なので、Config用のクラスの中で、User や UserRepository の import は行っていません)
■BatchTest1/src/main/resourses/application.properties
spring.application.name=BatchTest1
spring.datasource.url=jdbc:h2:./.data/h2/db;MODE=MySQL
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
■BatchTest1/src/main/java/com/netandfield/test/User.java
package com.netandfield.test;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity(name = "`user`")
@NoArgsConstructor
public class User {
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer id;
public String firstName;
public String lastName;
}
※注意
h2 データベースを MODE=MySQL で使っているせいか、テーブル名=user が MySQL の予約語 USER とかぶって expected "identifier" エラーとなってしまいます。
@Entity(name = "user") の user をバックスラッシュで囲んで、@Entity(name = "`user`") のようにしてください。
■BatchTest1/src/main/java/com/netandfield/test/UserRepository.java
package com.netandfield.test;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
■BatchTest1/src/main/java/com/netandfield/test/BatchTest1Application.java
package com.netandfield.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchTest1Application {
public static void main(String[] args) {
SpringApplication.run(BatchTest1Application.class, args);
}
}
■BatchTest1/src/main/java/com/netandfield/test/BatchTest1Configuration.java
package com.netandfield.test;
import java.text.Normalizer;
import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.job.parameters.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.infrastructure.item.ItemProcessor;
import org.springframework.batch.infrastructure.item.data.RepositoryItemWriter;
import org.springframework.batch.infrastructure.item.data.builder.RepositoryItemWriterBuilder;
import org.springframework.batch.infrastructure.item.file.FlatFileItemReader;
import org.springframework.batch.infrastructure.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.infrastructure.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.transaction.PlatformTransactionManager;
import lombok.extern.slf4j.Slf4j;
@Configuration
@Slf4j
public class BatchTest1Configuration {
@Autowired
public UserRepository userRepository;
/**
* sample.csv ファイルを読み込む
* @return バッチ処理用 Reader item
*/
@Bean
public FlatFileItemReader<User> reader() {
return new FlatFileItemReaderBuilder<User>()
.name("userItemReader")
.resource(new FileSystemResource("sample.csv"))
.delimited()
.names("firstName", "lastName")
.fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
setTargetType(User.class);
}})
.build();
}
/**
* Reader から渡されたオブジェクトを保存する形に変換する
* 今回はアルファベットの大文字変換と、半角カナを全角カナに変換している
* @return 変換後に保存するアイテム
*/
@Bean
public ItemProcessor<User, User> processor() {
return user -> {
final var firstName = Normalizer.normalize(user.getFirstName().toUpperCase(), Normalizer.Form.NFKC);
final var lastName = Normalizer.normalize(user.getLastName().toUpperCase(), Normalizer.Form.NFKC);
final var transformedUser = new User(firstName, lastName);
log.info("before converted : " + user);
log.info("transformed user : " + transformedUser);
return transformedUser;
};
}
/**
* Processor から渡されたオブジェクトを書き込む(保存処理をかける)
* @return 保存処理用のリポジトリ
*/
@Bean
public RepositoryItemWriter<User> writer() {
return new RepositoryItemWriterBuilder<User>()
.repository(userRepository)
.methodName("save")
.build();
}
/**
* 1つのバッチ処理の処理順やエラーハンドリング等の定義を行う
* @return Jobを返却
*/
@Bean
public Job importUserJob(JobRepository jobRepository, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.incrementer(new RunIdIncrementer())
.flow(step1)
.end()
.build();
}
/**
* Jobの中に含まれるステップを定義する
* @return ステップを返却する
*/
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("step1", jobRepository)
.<User, User>chunk(10)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
}
}
テストデータは、Kato83 さんのものをそのまま使っています。
■BatchTest1/sample.csv
ジョン, スミス
田中, 太郎
ジョセフ, ジョースター
Erik, Satie
これらのファイルを用意して、BatchTest1Application.java を Java アプリケーションとして実行します(俺は Eclipse 上で実行してるもんで)
これで、
2026-06-09T17:18:46.581+09:00 INFO 684 --- [BatchTest1] [ main] o.s.batch.core.step.AbstractStep : Executing step: [step1]
2026-06-09T17:18:46.648+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=ジョン, lastName=スミス)
2026-06-09T17:18:46.648+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ジョン, lastName=スミス)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=田中, lastName=太郎)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=田中, lastName=太郎)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=ジョセフ, lastName=ジョースター)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ジョセフ, lastName=ジョースター)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=Erik, lastName=Satie)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ERIK, lastName=SATIE)
2026-06-09T17:18:46.818+09:00 INFO 684 --- [BatchTest1] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 236ms
2026-06-09T17:18:46.821+09:00 INFO 684 --- [BatchTest1] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [FlowJob: [name=importUserJob]] completed with the following parameters: [{JobParameter{name='run.id', value=1, type=class java.lang.Long, identifying=true}}] and the following status: [COMPLETED] in 248ms
2026-06-09T17:18:46.831+09:00 INFO 684 --- [BatchTest1] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2026-06-09T17:18:46.648+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=ジョン, lastName=スミス)
2026-06-09T17:18:46.648+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ジョン, lastName=スミス)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=田中, lastName=太郎)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=田中, lastName=太郎)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=ジョセフ, lastName=ジョースター)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ジョセフ, lastName=ジョースター)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=Erik, lastName=Satie)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ERIK, lastName=SATIE)
2026-06-09T17:18:46.818+09:00 INFO 684 --- [BatchTest1] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 236ms
2026-06-09T17:18:46.821+09:00 INFO 684 --- [BatchTest1] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [FlowJob: [name=importUserJob]] completed with the following parameters: [{JobParameter{name='run.id', value=1, type=class java.lang.Long, identifying=true}}] and the following status: [COMPLETED] in 248ms
2026-06-09T17:18:46.831+09:00 INFO 684 --- [BatchTest1] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
※一部抜粋
このような結果が得られます。やった(笑)

コメントする