Using Job Composition, we can compose parent job using multiple child jobs.
Follow below steps to create a parent job with
a. Initialization Step
b. Child Job1
c. Child Job2
d. Cleanup Step
Step 1: Create Initialization Step.
@Bean
public Step parentJobInitializer() {
return this.stepBuilderFactory.get("Parent-Job-Initializer")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
Step 2: Create child job1.
@Bean
public Step childJob1Step1() {
return this.stepBuilderFactory.get("ChildJob1-Step1")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job childJob1() {
return jobBuilderFactory.get("childJob1").start(childJob1Step1()).build();
}
Step 3: Create Child Job 2.
@Bean
public Step childJob2Step1() {
return this.stepBuilderFactory.get("ChildJob2-Step1")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job childJob2() {
return jobBuilderFactory.get("childJob2").start(childJob2Step1()).build();
}
Step 4: Create Cleanup Step
@Bean
public Step parentJobCleanup() {
return this.stepBuilderFactory.get("Parent-Job-Cleanup")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
Step 5: Create parent job using Initialization step, childJob1, childjob2 and cleanup step.
To make job composition possible, we need to convert the jobs to steps. You can do this using JobStepBuilder.
Step childJob1Step = new JobStepBuilder(new StepBuilder("childJob1")).job(childJob1()).launcher(jobLauncher)
.repository(jobRepository).transactionManager(platformTransactionManager).build();
Step childJob2Step = new JobStepBuilder(new StepBuilder("childJob2")).job(childJob2()).launcher(jobLauncher)
.repository(jobRepository).transactionManager(platformTransactionManager).build();
Once you define the Child Job steps, create parent job.
@Bean
public Job parentJob(JobRepository jobRepository, PlatformTransactionManager platformTransactionManager) {
Step childJob1Step = new JobStepBuilder(new StepBuilder("childJob1")).job(childJob1()).launcher(jobLauncher)
.repository(jobRepository).transactionManager(platformTransactionManager).build();
Step childJob2Step = new JobStepBuilder(new StepBuilder("childJob2")).job(childJob2()).launcher(jobLauncher)
.repository(jobRepository).transactionManager(platformTransactionManager).build();
return jobBuilderFactory.get("parentJob").start(parentJobInitializer()).next(childJob1Step).next(childJob2Step)
.next(parentJobCleanup()).build();
}
Step 6: Set the jobs to be executed in application.properties by adding following property.
spring.batch.job.names=parentJob
Why do we add above property?
It is since Spring execute all the jobs by default, but we don’t want to execute child jobs independently. That’s why we explicitly mention spring batch to execute parentJob and ignore other jobs.
Find the below working application.
Step 1: Create new maven project ‘job-composition’.
Step 2: Update pom.xml with maven dependencies.
pom.xml
<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>com.sample.app</groupId>
<artifactId>job-composition</artifactId>
<version>1</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-core -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
</project>
Step 3: Create new package ‘com.sample.app.configuration’ and define JobConfiguration.
JobConfiguration.java
package com.sample.app.configuration;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
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.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.builder.JobStepBuilder;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.repeat.RepeatStatus;
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
@EnableBatchProcessing
public class JobConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private JobLauncher jobLauncher;
@Bean
public Step parentJobInitializer() {
return this.stepBuilderFactory.get("Parent-Job-Initializer")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step childJob1Step1() {
return this.stepBuilderFactory.get("ChildJob1-Step1")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job childJob1() {
return jobBuilderFactory.get("childJob1").start(childJob1Step1()).build();
}
@Bean
public Step childJob2Step1() {
return this.stepBuilderFactory.get("ChildJob2-Step1")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job childJob2() {
return jobBuilderFactory.get("childJob2").start(childJob2Step1()).build();
}
@Bean
public Step parentJobCleanup() {
return this.stepBuilderFactory.get("Parent-Job-Cleanup")
.tasklet((StepContribution contribution, ChunkContext chunkContext) -> {
String stepName = chunkContext.getStepContext().getStepName();
System.out.println(stepName + " is getting executed!!!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job parentJob(JobRepository jobRepository, PlatformTransactionManager platformTransactionManager) {
Step childJob1Step = new JobStepBuilder(new StepBuilder("childJob1")).job(childJob1()).launcher(jobLauncher)
.repository(jobRepository).transactionManager(platformTransactionManager).build();
Step childJob2Step = new JobStepBuilder(new StepBuilder("childJob2")).job(childJob2()).launcher(jobLauncher)
.repository(jobRepository).transactionManager(platformTransactionManager).build();
return jobBuilderFactory.get("parentJob").start(parentJobInitializer()).next(childJob1Step).next(childJob2Step)
.next(parentJobCleanup()).build();
}
}
Step 4: Define App.java
App.java
package com.sample.app;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableBatchProcessing
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Step 5: Create application.properties file under src/main/resources folder.
application.properties
logging.level.root=ERROR logging.level.org.hibernate=ERROR ## H2 specific properties spring.h2.console.enabled=true spring.h2.console.path=/h2 spring.datasource.url=jdbc:h2:file:~/db/myOrg.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; spring.datasource.username=krishna spring.datasource.password=password123 spring.datasource.driverClassName=org.h2.Driver ## JPA specific properties # Creates the schema, destroying previous data. spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.show-sql=false spring.jpa.properties.hibernate.format_sql=false ## Database connection pooling properties # Number of ms to wait before throwing an exception if no connection is available. spring.datasource.max-wait=10000 # Maximum number of active connections that can be allocated from this pool at the same time. spring.datasource.tomcat.max-active=10 spring.datasource.tomcat.max-idle=5 spring.datasource.tomcat.min-idle=3 spring.batch.job.names=parentJob
Total project structure looks like below.
Run App.java, you will see below messages in console.
Parent-Job-Initializer is getting executed!!!
ChildJob1-Step1 is getting executed!!!
ChildJob2-Step1 is getting executed!!!
Parent-Job-Cleanup is getting executed!!!
You can download complete working application from this link.
https://github.com/harikrishna553/springboot/tree/master/batch/job-composition
Previous Next Home
Nice Sample..Thanks. Do we have similar sample for chunk also
ReplyDelete