May 14, 2025
May 14, 2025
N/A Views
MD

Tip

この記事ではSpring Batchのメタデータを保存しない方法を紹介しますが、メタデータを視覚化するためのSpring Batch Dashboardも作ったので、メタデータを保存して有効活用することも検討してください。

Spring Batchでバッチ処理を作成する際に、DB上にメタデータが自動で作成されます。
メタデータを作成したくないという方向けにかつてはMapベースのインメモリJobRepositoryが提供されていましたが、Spring Batch 5で廃止されました。
代わりにH2やHSQLなどのインメモリDBを使うように案内されました。

Note

どちらの場合でもインメモリなJobRepositoryの場合はWebアプリケーションなど常駐プロセス上でバッチを起動する場合には、メモリが増え続けるという問題がありました。

Spring Batch 5.2では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)。

Spring Bootプロジェクトの作成

Spring Batchのプロジェクトを次のコマンドで作成します。

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に設定を追加します。

cat <<EOF >> src/main/resources/application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
EOF

代わりに、次のようにマニュアルでSpring Batchのコア機能の定義を行います。

cat <<EOF > 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を指定します。

cat <<EOF > 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を編集し、次のように設定します。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-jdbc</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

以上で設定は完了です。

実行

次のコマンドでビルドと実行を行います。

./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で実行する場合は、次のコマンドでビルドと実行を行います。

./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 

こちらも問題なく実行できました。

Found a mistake? Update the entry.
Share this article: