Java Batch Tutorial
1. Introduction
In this post, we feature a comprehensive Example on Java Batch. Java batch application is a Java application which processes a group of records, usually large volumes of data, as a single unit automatically. Spring introduced the Spring-batch framework in 2006. Spring Batch is an open source framework designed to enable the development of robust batch applications vital for the daily operations of enterprise systems. It has supported JSR 352 since version 3.0. In this example, I will demonstrate how to create a Java batch application in 15 minutes utilizing Spring Batch framework.
2. Technologies used
The example code in this article was built and run using:
- Java 1.8.101 (1.8.x will do fine)
- Maven 3.3.9 (3.3.x will do fine)
- Spring boot 1.5.14 ( Higher version will do fine)
- Spring Batch 3.0.5.RELEASE (4.0.0.M1 will do fine)
- Eclipse Mars (Any Java IDE would work)
3. Spring Boot Batch Application
The easiest way to generate a Spring-boot batch application is via the Spring starter tool with the steps below:
- Go to https://start.spring.io/.
- Select
Maven Project
withJava
and Spring Boot version 1.5.14 and type inBatch
,H2
in the “search for dependencies” bar. - Enter the group name as
jcg.demo.zheng
and artifact asspringboot-batch-demo
. - Click the
Generate Project
button.
A zip file – springboot-batch-demo.zip
will be generated and downloaded to your workstation.
4. Eclipse Maven Project
In this step, I will import the generated Spring boot batch project into my Eclipse workspace with these steps:
- Unzip the
springboot-batch-demo.zip
toC:\MZheng_Java_workspace\spingboot-batch-demo
. - Launch Eclipse.
- Click Import->Existing Maven Project.
- Browse to the
C:\MZheng_Java_workspace\spingboot-batch-demo
and click Finish.
A maven project – springboot-batch-demo
will be imported.
4.1 Maven Dependency
The generated pom.xml
includes spring-boot-starter-batch
and H2
database. There is no modification needed in this example.
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jcg.demo.zheng</groupId> <artifactId>springboot-batch-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-batch-demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.14.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
4.2 Spring Batch Application
The generated SpringbootBatchDemoApplication.java
is annotated with @SpringBootApplication
. It is equivalent to using @Configuration
, @EnableAutoConfiguration
, and @ComponentScan
with their default attributes.
In this step, I will add @EnableBatchProcessing
to enable Batch auto-configuration. By default, it executes all Jobs
in the application context on startup.
SpringbootBatchDemoApplication.java
package jcg.demo.zheng.springbootbatchdemo; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableBatchProcessing public class SpringbootBatchDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootBatchDemoApplication.class, args); } }
5. Spring Batch Framework Overview
Spring batch framework defines a batch domain language. In this example, we will stick to these terms to avoid confusion. Please click here for more details.
A simplified architecture diagram above shows the key components. The blue ones are Spring Batch Beans; the yellow ones are Spring Batch interfaces. Developers implement the ItemReader
, ItemWriter
, and ItemProcessor
interfaces based on the business processing logic. Then they wire them into batch Jobs
and Steps
. These batch jobs then will be executed by the Spring JobLauncher
.
5.1 Batch Steps
According to the Batch Domain language, a Step
is a domain object that encapsulates an independent, sequential phase of a batch job. A Step
contains all of the information necessary to define and control the actual batch processing. In this step, I will create a Reader-Processor-Writer step which reads messages, converts them into an uppercase format, and then outputs them to the console.
5.1.1 SimpleReader
SimpleReader
is a class which implements the ItemReader
. It reads a list of String
messages.
SimpleReader.java
package jcg.demo.zheng.springbootbatchdemo.step; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.NonTransientResourceException; import org.springframework.batch.item.ParseException; import org.springframework.batch.item.UnexpectedInputException; public class SimpleReader implements ItemReader { private String[] tenMessages = { "Message 1", "Message 2", "Message 3", "Message 4", "Message 5", "Message 6", "Message 7", "Message 8", "Message 9", "Message 10" }; private int count = 0; @Override public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { if (count < tenMessages.length) { return tenMessages[count++]; } else { count = 0; } return null; } }
5.1.2 SimpleWriter
SimpleWriter
is a class which implements the ItemWriter
. It writes out String
message.
SimpleWriter.java
package jcg.demo.zheng.springbootbatchdemo.step; import java.util.List; import org.springframework.batch.item.ItemWriter; public class SimpleWriter implements ItemWriter { @Override public void write(List<? extends String> messages) throws Exception { for (String msg : messages) { System.out.println("Writing the data " + msg); } } }
5.1.3 SimpleProcessor
SimpleProcessor
is a class which implements the ItemProcessor
. It converts the String
message to the uppercase format.
SimpleProcessor.java
package jcg.demo.zheng.springbootbatchdemo.step; import org.springframework.batch.item.ItemProcessor; public class SimpleProcessor implements ItemProcessor<String, String> { @Override public String process(String data) throws Exception { System.out.println("process for " + data); return data.toUpperCase(); } }
5.2 Batch Job Execution Listener
A JobExecution refers to the technical concept of a single attempt to run a Job. An execution may end in either failure or success. In this step, I will create a listener to output the job status after the job is executed.
JobCompletionListener.java
package jcg.demo.zheng.springbootbatchdemo.listener; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.listener.JobExecutionListenerSupport; import org.springframework.stereotype.Component; @Component public class JobCompletionListener extends JobExecutionListenerSupport { @Override public void afterJob(JobExecution jobExecution) { StringBuilder msg = new StringBuilder(); msg.append("##### Finishing Job Name=").append(jobExecution.getJobInstance().getJobName()) .append(" JOB_EXE_ID=").append(jobExecution.getId()).append(" JOB_ID=").append(jobExecution.getJobId()) .append(", Status=").append(jobExecution.getStatus()).append(", StartTime=") .append(jobExecution.getStartTime()).append(", EndTime=").append(jobExecution.getEndTime()); System.out.println(msg); } }
5.3 Batch Job and Step Configuration
5.3.1 SimpleAdapterReader
SimpleAdapterReader
is a class which implements the InitializingBean
.
SimpleAdapterReader.java
package jcg.demo.zheng.springbootbatchdemo.config; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; @Component public class SimpleAdapterReader implements InitializingBean { private List messages = new ArrayList(); @Override public void afterPropertiesSet() throws Exception { System.out.println("User bean initialized successfully..."); loadItems(); } private void loadItems() throws Exception { for (int i = 0; i 0) { return messages.remove(0); } else { System.out.println("No more item to read"); return null; } } }
A Job
is an entity that encapsulates the entire batch process.
In this step, I will configure two Spring batch jobs – simpleJob
and simpleAdapterJob
:
- Wire
simpleJob
withoneStep
which consists ofSimpleReader
,SimpleWriter
, andSimpleProcessor
with chunk sizes of three. - Wire
simpleAdapterJob
withadapterStep
which containssimpleAdaperReader
,SimpleWriter
, andSimpleProcessor
with chunk sizes of three.
BatchConfig.java
package jcg.demo.zheng.springbootbatchdemo.config; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecutionListener; import org.springframework.batch.core.Step; 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.batch.item.adapter.ItemReaderAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import jcg.demo.zheng.springbootbatchdemo.step.SimpleProcessor; import jcg.demo.zheng.springbootbatchdemo.step.SimpleReader; import jcg.demo.zheng.springbootbatchdemo.step.SimpleWriter; @Configuration public class BatchConfig { @Autowired public JobBuilderFactory jobBuilderFactory; @Autowired public StepBuilderFactory stepBuilderFactory; @Autowired public JobExecutionListener listener; @Autowired public SimpleAdapterReader simpleAdapter; private int STEP_CHUNK_SIZE = 3; private static final String SIMPLE_JOB_NAME = "simpleJob"; private static final String STEP_ONE_NAME = "oneStep"; private static final String STEP_A_NAME = "adapterStep"; @Bean public Job simpleJob() { return jobBuilderFactory.get(SIMPLE_JOB_NAME).incrementer(new RunIdIncrementer()).listener(listener) .flow(oneStep()).end().build(); } @Bean public Job simpleAdapterJob() { return jobBuilderFactory.get("simpleAdapterJob").incrementer(new RunIdIncrementer()).listener(listener) .flow(adapterStep()).end().build(); } @Bean public Step oneStep() { return stepBuilderFactory.get(STEP_ONE_NAME). chunk(STEP_CHUNK_SIZE).reader(new SimpleReader()) .processor(new SimpleProcessor()).writer(new SimpleWriter()).build(); } @Bean public Step adapterStep() { return stepBuilderFactory.get(STEP_A_NAME). chunk(STEP_CHUNK_SIZE).reader(simpleAdaperReader()) .processor(new SimpleProcessor()).writer(new SimpleWriter()).build(); } @Bean public ItemReaderAdapter simpleAdaperReader() { ItemReaderAdapter adapter = new ItemReaderAdapter(); adapter.setTargetObject(simpleAdapter); adapter.setTargetMethod("nextItem"); return adapter; } }
6. Demo
Start the SpringbootBatchDemoApplication
, we will see that both Batch jobs are executed.
SpringbootBatchDemoApplication Output
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.14.RELEASE) 2018-06-20 17:10:18.570 INFO 11712 --- [ main] j.d.z.s.SpringbootBatchDemoApplication : Starting SpringbootBatchDemoApplication on SL2LS431841 with PID 11712 (C:\MZheng_Java_workspace\springboot-batch-demo\target\classes started by Shu.Shan in C:\MZheng_Java_workspace\springboot-batch-demo) 2018-06-20 17:10:18.573 INFO 11712 --- [ main] j.d.z.s.SpringbootBatchDemoApplication : No active profile set, falling back to default profiles: default 2018-06-20 17:10:18.675 INFO 11712 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27c6e487: startup date [Wed Jun 20 17:10:18 CDT 2018]; root of context hierarchy User bean initialized successfully... 2018-06-20 17:10:21.054 INFO 11712 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from class path resource [org/springframework/batch/core/schema-h2.sql] 2018-06-20 17:10:21.139 INFO 11712 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executed SQL script from class path resource [org/springframework/batch/core/schema-h2.sql] in 83 ms. 2018-06-20 17:10:21.416 INFO 11712 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2018-06-20 17:10:21.443 INFO 11712 --- [ main] o.s.b.a.b.JobLauncherCommandLineRunner : Running default command line with: [] 2018-06-20 17:10:21.461 INFO 11712 --- [ main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: H2 2018-06-20 17:10:21.820 INFO 11712 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor. 2018-06-20 17:10:21.975 INFO 11712 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=simpleJob]] launched with the following parameters: [{run.id=1}] 2018-06-20 17:10:22.023 INFO 11712 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [oneStep] process for Message 1 process for Message 2 process for Message 3 Writing the data MESSAGE 1 Writing the data MESSAGE 2 Writing the data MESSAGE 3 process for Message 4 process for Message 5 process for Message 6 Writing the data MESSAGE 4 Writing the data MESSAGE 5 Writing the data MESSAGE 6 process for Message 7 process for Message 8 process for Message 9 Writing the data MESSAGE 7 Writing the data MESSAGE 8 Writing the data MESSAGE 9 process for Message 10 Writing the data MESSAGE 10 ##### Finishing Job Name=simpleJob JOB_EXE_ID=1 JOB_ID=1, Status=COMPLETED, StartTime=Wed Jun 20 17:10:21 CDT 2018, EndTime=Wed Jun 20 17:10:22 CDT 2018 2018-06-20 17:10:22.083 INFO 11712 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=simpleJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] 2018-06-20 17:10:22.095 INFO 11712 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=simpleAdapterJob]] launched with the following parameters: [{run.id=1}] 2018-06-20 17:10:22.108 INFO 11712 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [adapterStep] process for SimpleAdatperReader Message 0 process for SimpleAdatperReader Message 1 process for SimpleAdatperReader Message 2 Writing the data SIMPLEADATPERREADER MESSAGE 0 Writing the data SIMPLEADATPERREADER MESSAGE 1 Writing the data SIMPLEADATPERREADER MESSAGE 2 process for SimpleAdatperReader Message 3 process for SimpleAdatperReader Message 4 process for SimpleAdatperReader Message 5 Writing the data SIMPLEADATPERREADER MESSAGE 3 Writing the data SIMPLEADATPERREADER MESSAGE 4 Writing the data SIMPLEADATPERREADER MESSAGE 5 process for SimpleAdatperReader Message 6 process for SimpleAdatperReader Message 7 process for SimpleAdatperReader Message 8 Writing the data SIMPLEADATPERREADER MESSAGE 6 Writing the data SIMPLEADATPERREADER MESSAGE 7 Writing the data SIMPLEADATPERREADER MESSAGE 8 process for SimpleAdatperReader Message 9 process for SimpleAdatperReader Message 10 process for SimpleAdatperReader Message 11 Writing the data SIMPLEADATPERREADER MESSAGE 9 Writing the data SIMPLEADATPERREADER MESSAGE 10 Writing the data SIMPLEADATPERREADER MESSAGE 11 process for SimpleAdatperReader Message 12 process for SimpleAdatperReader Message 13 process for SimpleAdatperReader Message 14 Writing the data SIMPLEADATPERREADER MESSAGE 12 Writing the data SIMPLEADATPERREADER MESSAGE 13 Writing the data SIMPLEADATPERREADER MESSAGE 14 process for SimpleAdatperReader Message 15 process for SimpleAdatperReader Message 16 process for SimpleAdatperReader Message 17 Writing the data SIMPLEADATPERREADER MESSAGE 15 Writing the data SIMPLEADATPERREADER MESSAGE 16 Writing the data SIMPLEADATPERREADER MESSAGE 17 No more item to read process for SimpleAdatperReader Message 18 process for SimpleAdatperReader Message 19 Writing the data SIMPLEADATPERREADER MESSAGE 18 Writing the data SIMPLEADATPERREADER MESSAGE 19 ##### Finishing Job Name=simpleAdapterJob JOB_EXE_ID=2 JOB_ID=2, Status=COMPLETED, StartTime=Wed Jun 20 17:10:22 CDT 2018, EndTime=Wed Jun 20 17:10:22 CDT 2018 2018-06-20 17:10:22.148 INFO 11712 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=simpleAdapterJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] 2018-06-20 17:10:22.150 INFO 11712 --- [ main] j.d.z.s.SpringbootBatchDemoApplication : Started SpringbootBatchDemoApplication in 4.066 seconds (JVM running for 5.163) 2018-06-20 17:10:22.150 INFO 11712 --- [ Thread-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@27c6e487: startup date [Wed Jun 20 17:10:18 CDT 2018]; root of context hierarchy 2018-06-20 17:10:22.153 INFO 11712 --- [ Thread-3] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
7. Summary
In this example, we demonstrated how to build a Java batch application with Spring Batch framework in five steps:
- Generate the Spring boot batch application.
- Import the generated project into Eclipse IDE.
- Create implementation classes for Spring Batch
ItemReader
,ItemWriter
, andItemProcessor
Interfaces. - Configure the Spring batch
Job
andStep
with Java based configuration. - Start the Spring boot application to execute Spring batch jobs.
8. Download the Source Code
This example consists of a Java Batch application.
You can download the full source code of this example here: springboot-batch-demo
SimpleWriter, SimpleAdapterReader classes not matching the code.