--- title: Spring BatchのResourcelessJobRepositoryを使いメタデータを保存しないようにするメモ tags: ["Spring Batch", "Spring Boot", "Java", "Spring"] categories: ["Programming", "Java", "org", "springframework", "batch"] date: 2025-05-14T17:25:12Z updated: 2025-05-14T23:51:45Z --- > [!TIP] > この記事ではSpring Batchのメタデータを保存しない方法を紹介しますが、メタデータを視覚化するための[Spring Batch Dashboard](https://github.com/making/spring-batch-dashboard)も作ったので、メタデータを保存して有効活用することも検討してください。 Spring Batchでバッチ処理を作成する際に、DB上にメタデータが自動で作成されます。 メタデータを作成したくないという方向けにかつてはMapベースのインメモリ`JobRepository`が提供されていましたが、Spring Batch 5で廃止されました。 代わりにH2やHSQLなどのインメモリDBを使うように案内されました。 > [!NOTE] > どちらの場合でもインメモリな`JobRepository`の場合はWebアプリケーションなど常駐プロセス上でバッチを起動する場合には、メモリが増え続けるという問題がありました。 [Spring Batch 5.2](https://spring.io/blog/2024/11/20/spring-batch-5-2-0-goes-ga)では`JobRepository`の実装として、MongoDBに加え、メタデータを保存しない`ResourcelessJobRepository`が追加されました。 本記事では`ResourcelessJobRepository`を使いメタデータを保存しないSpring Batchの使用例を紹介します。 > [!WARNING] > メタデータはJobのリスタートにも使われるので、`ResourcelessJobRepository`を使用する場合は、Jobのリスタートはできません。また、`ResourcelessJobRepository`はスレッドセーフでもありません。 本記事の内容はSpring Boot 3.4.5、Spring Batch 5.2.2で動作確認しました。このバージョンではSpring BatchのAutoConfigurationがデータベースを使用する前提となっています。 そのため、`ResourcelessJobRepository`を使用する場合はAutoConfigurationを無効にする必要があります。これはSpring Batch 6、Spring Boot 4では改善される予定です([spring-batch#4718](https://github.com/spring-projects/spring-batch/issues/4718))。 ## Spring Bootプロジェクトの作成 Spring Batchのプロジェクトを次のコマンドで作成します。 ```bash curl https://start.spring.io/starter.tgz \ -d type=maven-project \ -d artifactId=hello-spring-batch \ -d baseDir=hello-spring-batch \ -d packageName=com.example.hello \ -d dependencies=batch,native \ -d applicationName=HelloSpringBatchApplication | tar -xzvf - cd hello-spring-batch ``` ## Spring BatchのAutoConfigurationを無効にする 前述の通り、Spring BatchのAutoConfigurationを無効にする必要があります。次のように`application.properties`に設定を追加します。 ```properties cat <> src/main/resources/application.properties spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration EOF ``` 代わりに、次のようにマニュアルでSpring Batchのコア機能の定義を行います。 ```java cat < src/main/java/com/example/hello/BatchConfig.java package com.example.hello; import java.lang.reflect.Proxy; import org.springframework.batch.core.configuration.support.ScopeConfiguration; import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.support.ResourcelessJobRepository; import org.springframework.boot.autoconfigure.batch.BatchProperties; import org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.util.StringUtils; @Configuration(proxyBeanMethods = false) @Import(ScopeConfiguration.class) @EnableConfigurationProperties(BatchProperties.class) public class BatchConfig { @Bean public JobRepository jobRepository() { return new ResourcelessJobRepository(); } @Bean public JobLauncher jobLauncher(JobRepository jobRepository) { TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher(); jobLauncher.setJobRepository(jobRepository); return jobLauncher; } @Bean public JobLauncherApplicationRunner jobLauncherApplicationRunner(JobLauncher jobLauncher, JobRepository jobRepository, BatchProperties properties) { JobExplorer jobExplorer = (JobExplorer) Proxy.newProxyInstance(JobExplorer.class.getClassLoader(), new Class[] { JobExplorer.class }, (proxy, method, args) -> null); JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobLauncher, jobExplorer, jobRepository); String jobName = properties.getJob().getName(); if (StringUtils.hasText(jobName)) { runner.setJobName(jobName); } return runner; } } EOF ``` ## Jobの定義 次のようにJobを定義します。ここはSpring Batchの基本的な使い方と同じですが、`PlatformTransactionManager`の実装として`ResourcelessTransactionManager`を指定します。 ```java cat < src/main/java/com/example/hello/JobConfig.java package com.example.hello; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.StepScope; 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.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.batch.support.transaction.ResourcelessTransactionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class JobConfig { private final Logger log = LoggerFactory.getLogger(JobConfig.class); private final JobRepository jobRepository; public JobConfig(JobRepository jobRepository) { this.jobRepository = jobRepository; } @Bean @StepScope public Tasklet helloTasklet() { return (contribution, chunkContext) -> { log.info("Hello World!"); return RepeatStatus.FINISHED; }; } @Bean public Step step1(Tasklet helloTasklet) { return new StepBuilder("step1", jobRepository).tasklet(helloTasklet, new ResourcelessTransactionManager()) .build(); } @Bean public Job job1(Step step1) { return new JobBuilder("job1", jobRepository).start(step1).build(); } } EOF ``` 今回はTasklet内でデータベースは扱わないので、Spring Batchにデフォルトで含まれる`spring-boot-starter-jdbc`の依存を除外します。pom.xmlを編集し、次のように設定します。 ```xml org.springframework.boot spring-boot-starter-batch org.springframework.boot spring-boot-starter-jdbc ``` 以上で設定は完了です。 ## 実行 次のコマンドでビルドと実行を行います。 ```bash ./mvnw clean package -DskipTests java -jar target/hello-spring-batch-0.0.1-SNAPSHOT.jar ``` ``` . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.4.5) 2025-05-15T01:56:40.031+09:00 INFO 7358 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Starting HelloSpringBatchApplication v0.0.1-SNAPSHOT using Java 21.0.6 with PID 7358 (/private/tmp/hello-spring-batch/target/hello-spring-batch-0.0.1-SNAPSHOT.jar started by toshiaki in /private/tmp/hello-spring-batch) 2025-05-15T01:56:40.032+09:00 INFO 7358 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : No active profile set, falling back to 1 default profile: "default" 2025-05-15T01:56:40.193+09:00 INFO 7358 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor. 2025-05-15T01:56:40.245+09:00 INFO 7358 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Started HelloSpringBatchApplication in 0.353 seconds (process running for 0.524) 2025-05-15T01:56:40.246+09:00 INFO 7358 --- [demo] [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [] 2025-05-15T01:56:40.247+09:00 INFO 7358 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{}] 2025-05-15T01:56:40.249+09:00 INFO 7358 --- [demo] [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step1] 2025-05-15T01:56:40.252+09:00 INFO 7358 --- [demo] [ main] com.example.hello.JobConfig : Hello World! 2025-05-15T01:56:40.253+09:00 INFO 7358 --- [demo] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 3ms 2025-05-15T01:56:40.254+09:00 INFO 7358 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 4ms ``` 無事に起動できました。 ## GraalVM Native Imageでの実行 GraalVM Native Imageで実行する場合は、次のコマンドでビルドと実行を行います。 ```bash ./mvnw native:compile -Pnative -DskipTests ./target/hello-spring-batch ``` ``` . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.4.5) 2025-05-15T01:58:15.389+09:00 INFO 7554 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Starting AOT-processed HelloSpringBatchApplication using Java 21.0.6 with PID 7554 (/private/tmp/hello-spring-batch/target/hello-spring-batch started by toshiaki in /private/tmp/hello-spring-batch) 2025-05-15T01:58:15.389+09:00 INFO 7554 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : No active profile set, falling back to 1 default profile: "default" 2025-05-15T01:58:15.391+09:00 INFO 7554 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor. 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Started HelloSpringBatchApplication in 0.01 seconds (process running for 0.019) 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [] 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{}] 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step1] 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] com.example.hello.JobConfig : Hello World! 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] completed with the following parameters: [{}] and the following status: [COMPLETED] in ``` こちらも問題なく実行できました。