Spring Batch

1. Concepts of Job, Step, Tasklet

You should know one basic concept of them. that is one of the relationship between each them.

  • ‘Job’ includes ‘Step’
  • ‘Step’ includes ‘Tasklet’

so You can remember the relationship between them like the below.

1
Job > Step > Tasket

Job

  • It’s like a container for Step.
  • It represents the one time of batch processing.

Step

  • It’s the object has components that is needed to batch processing.
  • It’s actual used component when the batch system is operated.

Tasklet

  • Tasklet can have the business logic what you want to do. so what i’m saying is you just type your code in there.

2. Let’s build simple spring batch with Tasklet.

  • add dependencies to process spring batch.
1
2
3
4
5
6
7
8
9
10
11
12
13

// build.gradle

implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'com.h2database:h2:1.4.197'

compileOnly 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'

annotationProcessor 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
  • turn on batch processing with @EnableBatchProcessing annotation.
1
2
3
4
5
6
7
8
9
@EnableBatchProcessing  // add this annotation.
@SpringBootApplication
public class BatchExampleApplication {

public static void main(String[] args) {
SpringApplication.run(BatchExampleApplication.class, args);
}

}
  • create custom Tasklet has the business logics you want.
1
2
3
4
5
6
7
8
@Slf4j
public class SampleTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("executed tasklet !!");
return RepeatStatus.FINISHED;
}
}
  • Basically, You should create job, step to operate batch processing. the below code is for that. as you can see, job includes step and step includes tasklet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
@RequiredArgsConstructor
public class SampleConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

@Bean
public Job sampleJob() {
return jobBuilderFactory.get("sampleJob")
.start(sampleStep())
.build();
}

@Bean
public Step sampleStep() {
return stepBuilderFactory.get("sampleStep")
.tasklet(new SampleTasklet())
.build();
}
}
1
2
3
4
5
6
7
2022-01-24 10:24:57.075  INFO 12160 --- [           main] c.k.y.b.BatchExampleApplication          : Started BatchExampleApplication in 0.907 seconds (JVM running for 1.273)
2022-01-24 10:24:57.076 INFO 12160 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2022-01-24 10:24:57.123 INFO 12160 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=sampleJob]] launched with the following parameters: [{}]
2022-01-24 10:24:57.142 INFO 12160 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [sampleStep]
2022-01-24 10:24:57.147 INFO 12160 --- [ main] c.kian.yun.batch_example.SampleTasklet : executed tasklet !!
2022-01-24 10:24:57.151 INFO 12160 --- [ main] o.s.batch.core.step.AbstractStep : Step: [sampleStep] executed in 8ms
2022-01-24 10:24:57.154 INFO 12160 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=sampleJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 18ms

you can see the log created in SampleTasklet at the console.

3. the three ways to implement Tasklet.

as you see before, the first way is that creating sub class of Tasklet class like SampleTasklet.

1
2
3
4
5
6
7
public class SampleTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("executed tasklet !!");
return RepeatStatus.FINISHED;
}
}

second way is the best simple one. It is that you create sub class of Tasklet with lambda expression.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
@RequiredArgsConstructor
public class SampleConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

// ...

@Bean
public Job lambdaJob() {
return jobBuilderFactory.get("lambdaJob")
.start(lambdaStep())
.build();
}

@Bean
public Step lambdaStep() {
return stepBuilderFactory.get("lambdaStep")
.tasklet((contribution, chunkContext) -> {
log.info("executed tasklet created by lambda !!");
return RepeatStatus.FINISHED;
}).build();
}
}

Last way would able to use the special case like you need the function is included at other service.

1
2
3
4
5
public class OtherService {
public void businessLogic() {
log.info("executed Tasklet in OtherService.businessLogic !!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Configuration
@RequiredArgsConstructor
public class SampleConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

// ...

@Bean
public Job invokedJob() {
return jobBuilderFactory.get("invokedJob")
.start(invokedStep())
.build();
}

@Bean
public Step invokedStep() {
return stepBuilderFactory.get("invokedStep")
.tasklet(invokedTasklet())
.build();
}

@Bean
public OtherService otherService() {
return new OtherService();
}

@Bean
public MethodInvokingTaskletAdapter invokedTasklet() {
MethodInvokingTaskletAdapter adapter = new MethodInvokingTaskletAdapter();

adapter.setTargetObject(otherService());
adapter.setTargetMethod("businessLogic");

return adapter;
}
}

4. configure step with flow

Spring batch offers the service that can control batch execution by the condition of other each step status.

  • on() function decides whether the to() function located on the below should be executed or not by the ExitStatus condition.

  • “*” condition means that every requests will be passed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Configuration
@RequiredArgsConstructor
public class SampleConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

// ...

@Bean
public Job flowJob() {
return jobBuilderFactory.get("flowJob")
.start(conditionStep())
.on("COMPLETED")
.to(completedStep())
.on("*")
.end()

.from(conditionStep())
.on("FAILED")
.to(failedStep())
.on("*")
.end()

.end().build();

}

@Bean
public Step conditionStep() {
return stepBuilderFactory.get("conditionStep")
.tasklet((contribution, chunkContext) -> {
log.info("condition step !!");

contribution.setExitStatus(ExitStatus.COMPLETED);

return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step completedStep() {
return stepBuilderFactory.get("completedStep")
.tasklet((contribution, chunkContext) -> {
log.info("completed step !!");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step failedStep() {
return stepBuilderFactory.get("failedStep")
.tasklet((contribution, chunkContext) -> {
log.info("failed step !!");
return RepeatStatus.FINISHED;
}).build();
}
}

5. About Chunk

Share