きっかけ
spring batchについて学習しようと思い、spring batch入門について調べていました。
ですが、ハローワールド実行までが難航してしまったのでここに記載します。
内容
spring boot2(batch4)のサポートが終了しているので、spring boot3(batch5)でハローワールドを実行しつつ、boot2との差分を比較するといった内容になっています。
問題
- batch4とbatch5でコードが変わる
batch4での記述がbatch5でも使えたら問題なかったのですが、一部使えなくなっていました。例:JobBuilderFactory, StepBuilderFactory
下図はboot2ではインポートできるがboot3ではインポートができないという状態です。
(JobBuilderFactoryなどはboot3では非推奨という記事は見かけましたが、使えなくなったとは記載がなかったのでIDE側が原因かもしれないです。※IDEはIntellijを使用。使えたとしても非推奨なのでコード変更の対応が必要)
spring boot3.4.0(batch5), JDK23を使用
spring boot2.4.2(batch4), JDK14を使用
プログラムファイル
今回プログラムを記載したファイルは下記の3種になります。
- \src\main\java\com\example\demo\
- DemoApplication.java
- HelloTasklet.java
- BatchConfig.java
batch5対応の記述
変更前
package com.example.demo;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private HelloTasklet helloTasklet;
@Bean
public Step sampleStep() {
return stepBuilderFactory.get("HelloStep")
.tasklet(helloTasklet)
.build();
}
@Bean
public Job sampleJob() throws Exception {
return jobBuilderFactory.get("HelloJob")
.incrementer(new RunIdIncrementer())
.start(sampleStep())
.build();
}
}
変更後
package com.example.demo;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class BatchConfig {
@Autowired
private HelloTasklet helloTasklet;
@Bean
public Step sampleStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("HelloStep", jobRepository)
.tasklet(helloTasklet, transactionManager)
.build();
}
@Bean
public Job sampleJob(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
return new JobBuilder("HelloJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(sampleStep(jobRepository, transactionManager))
.build();
}
}
変更内容について
○○BuildFactory
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
// 略
@Bean
public Step sampleStep() {
return stepBuilderFactory.get("HelloStep")
.tasklet(helloTasklet)
.build();
}
@Bean
public Job sampleJob() throws Exception {
return jobBuilderFactory.get("HelloJob")
.incrementer(new RunIdIncrementer())
.start(sampleStep())
.build();
}
stepBuilderFactoryがStepBuilder, jobBuilderFacotryがJobBuilderに変わっています。
上記2種のFactoryを使用するのに@EnableBatchProcessingが必要でしたが、Factoryを使わなくなったのでこのアノテーションも削除しています。
※taskletについては後述
// import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
// 略
@Bean
public Step sampleStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("HelloStep", jobRepository)
.tasklet(helloTasklet, transactionManager)
.build();
}
@Bean
public Job sampleJob(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
return new JobBuilder("HelloJob", jobRepository)
.incrementer(new RunIdIncrementer())
.start(sampleStep(jobRepository, transactionManager))
.build();
}
tasklet
package com.example.demo.tasklet;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Component
@StepScope
public class HelloTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("Hello World");
return RepeatStatus.FINISHED;
}
}
HelloTasklet.javaはbatch4, 5でコードに変更はありません。
下記はBatchConfig.javaの内容です。
@Bean
public Step sampleStep() {
return stepBuilderFactory.get("HelloStep")
.tasklet(helloTasklet)
.build();
}
taskletにPlatformTransactionManagerが要求されるようになりました。
@Bean
public Step sampleStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("HelloStep", jobRepository)
.tasklet(helloTasklet, transactionManager)
.build();
}
実行結果
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplicationが付与されているクラスを実行すると、下記内容が出力されるのを確認できると思います。
2024-12-12T00:24:11.148+09:00 INFO 25432 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=HelloJob]] launched with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}]
2024-12-12T00:24:11.164+09:00 INFO 25432 --- [demo] [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [HelloStep]
Hello World
2024-12-12T00:24:11.174+09:00 INFO 25432 --- [demo] [ main] o.s.batch.core.step.AbstractStep : Step: [HelloStep] executed in 8ms
2024-12-12T00:24:11.178+09:00 INFO 25432 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=HelloJob]] completed with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 21ms
2024-12-12T00:24:11.181+09:00 INFO 25432 --- [demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
上記のようなログが出ない場合
BatchConfig.javaに@EnableBatchProcessingが付与されているとうまく実行できないみたいで、下記のようなログのみ出力され上記のようにどのバッチ、ステップが呼ばれたかなどは出力されませんでした。また、この動きに対してエラーはでませんでした。(デバッグするとjobやstepの呼び出しはしているが、taskletは呼ばれていないのが確認できます。これにずっとハマってました)
2024-12-12T00:31:02.411+09:00 INFO 23436 --- [demo] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2024-12-12T00:31:02.420+09:00 INFO 23436 --- [demo] [ main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: H2
2024-12-12T00:31:02.449+09:00 INFO 23436 --- [demo] [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP
2024-12-12T00:31:02.453+09:00 INFO 23436 --- [demo] [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP
2024-12-12T00:31:02.456+09:00 INFO 23436 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
2024-12-12T00:31:02.521+09:00 INFO 23436 --- [demo] [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.968 seconds (process running for 1.201)
2024-12-12T00:31:02.524+09:00 INFO 23436 --- [demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-12-12T00:31:02.546+09:00 INFO 23436 --- [demo] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.0'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(23)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-batch'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.batch:spring-batch-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
spring batch使うならおそらくDBが必要です。DBがないと実行時にエラーになりました。
上記だと下記がDBに該当します。
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.hsqldb:hsqldb'でもOK、他は未確認。