Warning
This article was automatically translated by OpenAI (gpt-4.1).It may be edited eventually, but please be aware that it may contain incorrect information at this time.
Tip
This article introduces a way to avoid saving metadata in Spring Batch, but I also created a Spring Batch Dashboard to visualize metadata, so please consider saving and making good use of metadata as well.
When creating batch processes with Spring Batch, metadata is automatically created in the database.
For those who do not want to create metadata, a Map-based in-memory JobRepository was previously provided, but it was removed in Spring Batch 5.
Instead, it is now recommended to use in-memory databases such as H2 or HSQL.
Note
In either case, when using an in-memory JobRepository, if you start the batch on a resident process such as a web application, there was a problem where memory usage would continue to increase.
In Spring Batch 5.2, in addition to MongoDB, a ResourcelessJobRepository implementation that does not save metadata was added.
This article introduces an example of using Spring Batch without saving metadata by using ResourcelessJobRepository.
Warning
Metadata is also used for job restarts, so if you use ResourcelessJobRepository, you cannot restart jobs. Also, ResourcelessJobRepository is not thread-safe.
The content of this article was confirmed to work with Spring Boot 3.4.5 and Spring Batch 5.2.2. In these versions, Spring Batch's AutoConfiguration assumes the use of a database.
Therefore, if you want to use ResourcelessJobRepository, you need to disable AutoConfiguration. This is planned to be improved in Spring Batch 6 and Spring Boot 4 (spring-batch#4718).
Creating a Spring Boot Project
Create a Spring Batch project with the following command.
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
Disabling Spring Batch AutoConfiguration
As mentioned above, you need to disable Spring Batch's AutoConfiguration. Add the following setting to application.properties.
cat <<EOF >> src/main/resources/application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
EOF
Instead, manually define the core features of Spring Batch as follows.
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
Defining the Job
Define the job as follows. This is the same as the basic usage of Spring Batch, but specify ResourcelessTransactionManager as the implementation of PlatformTransactionManager.
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
Since we are not handling a database inside the Tasklet this time, exclude the dependency on spring-boot-starter-jdbc that is included by default in Spring Batch. Edit pom.xml and set it as follows.
<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>
That's all for the setup.
Running
Build and run with the following commands.
./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
It started up successfully.
Running with GraalVM Native Image
To run with GraalVM Native Image, build and run with the following commands.
./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
This also ran without any issues.