배경 : 스프링배치 10주차 과제 를 수행하기 위한 정리를 진행함.
참고 :
1.10주차 교재
(아래 글은 한국 스프링 사용자 모임(KSUG)에서 진행된 스프링 배치 스터디 내용을 정리한 게시글입니다.
DEVOCEAN에 연재 중인 KIDO님의 글을 참고하여 실습한 내용을 기록했습니다.)
https://devocean.sk.com/blog/techBoardDetail.do?ID=167054
2. 예제 소스
https://github.com/schooldevops/spring-batch-tutorials/tree/10.01.FlowControl/10.01.FlowControlample
Spring Batch Flow Controller
- 배치 수행 Flow Controll 은 여러 Step 을 정의하고, 조건에 따라 순서대로 실행, 특정 Step 은 건너 뛸 수 있도록 하는 기능.
- FlowBuilder API 사용하여 설정
Flow Controll 방법
- next : 현재 Step이 성공적 종료되면 다음 Step 이동
- from : 특정 Step 에서 현재 Step 으로 이동
- on : 특정 ExitStatus에 따라 다음 Step을 결정
- to : 특정 Step 으로 이동
- stop : 현재 Flow를 종료
- end : FlowBuilder를 종료
Flow 컨트롤 샘플 코드
next
- Start 스탭 수행 뒤, next 스탭으로 이동
- next 는 계속해서 추가 가능 (start --> next --> next ... 순으로 진행)
NextStepTaskJobConfiguration.java
package com.ksko.spring_batch.batch_sample.jobs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.scope.context.ChunkContext;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Slf4j
@Configuration
public class NextStepTaskJobConfiguration {
public static final String NEXT_STEP_TASK = "NEXT_STEP_TASK";
@Autowired
PlatformTransactionManager transactionManager;
@Bean(name = "step01")
public Step step01(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("step01", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 01 Tasklet ...");
return RepeatStatus.FINISHED;
}
}, transactionManager)
.build();
}
@Bean(name = "step02")
public Step step02(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("step02", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 02 Tasklet ...");
return RepeatStatus.FINISHED;
}
}, transactionManager)
.build();
}
@Bean
public Job nextStepJob(Step step01, Step step02, JobRepository jobRepository) {
log.info("------------------ Init myJob -----------------");
return new JobBuilder(NEXT_STEP_TASK, jobRepository)
.incrementer(new RunIdIncrementer())
.start(step01)
.next(step02)
.build();
}
}
실행결과
2024-12-16T21:40:30.166+09:00 INFO 3500 --- [ main] c.k.s.b.j.m.MybatisItemWriterJobConfig : ------------------ Init flatFileStep -----------------
2024-12-16T21:40:30.191+09:00 INFO 3500 --- [ main] c.k.s.b.j.m.MybatisItemWriterJobConfig : ------------------ Init flatFileJob -----------------
2024-12-16T21:40:30.197+09:00 INFO 3500 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T21:40:30.200+09:00 INFO 3500 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T21:40:30.202+09:00 INFO 3500 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T21:40:30.344+09:00 INFO 3500 --- [ main] c.k.s.b.BatchSampleApplication : Started BatchSampleApplication in 2.86 seconds (process running for 3.408)
2024-12-16T21:40:30.347+09:00 INFO 3500 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2024-12-16T21:40:30.716+09:00 INFO 3500 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=NEXT_STEP_TASK]] launched with the following parameters: [{'run.id':'{value=2, type=class java.lang.Long, identifying=true}'}]
2024-12-16T21:40:30.887+09:00 INFO 3500 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step01]
2024-12-16T21:40:30.955+09:00 INFO 3500 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : Execute Step 01 Tasklet ...
2024-12-16T21:40:31.064+09:00 INFO 3500 --- [ main] o.s.batch.core.step.AbstractStep : Step: [step01] executed in 175ms
2024-12-16T21:40:31.250+09:00 INFO 3500 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step02]
2024-12-16T21:40:31.319+09:00 INFO 3500 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : Execute Step 02 Tasklet ...
2024-12-16T21:40:31.376+09:00 INFO 3500 --- [ main] o.s.batch.core.step.AbstractStep : Step: [step02] executed in 124ms
2024-12-16T21:40:31.466+09:00 INFO 3500 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=NEXT_STEP_TASK]] completed with the following parameters: [{'run.id':'{value=2, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 702ms
2024-12-16T21:40:31.471+09:00 INFO 3500 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-12-16T21:40:31.473+09:00 INFO 3500 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-12-16T21:40:33.284+09:00 INFO 3500 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
step01 -> step02 순차 진행된 것을 확인 할 수 있다.
on
- 특정 스탭의 종료 조건에 따라 어떤 Step 으로 이동할지 결정 가능
- 아래 예제는 Step01 먼저 수행 --> 해당 결과에 따라 다음 Step 이동 하는 플로우
- on("FAILED") 인 경우, step03 수행
- from(step01).on("COMPLETED") 인 경우 step03 의 결과 완료 라면, step02 수행
- on 과 from 통해 Step 의 종료 조건에 따라 원하는 플로우 처리 가능
OnStepTaskJobConfiguration.java
package com.ksko.spring_batch.batch_sample.jobs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.scope.context.ChunkContext;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import java.util.Random;
@Slf4j
@Configuration
public class OnStepTaskJobConfiguration {
public static final String ON_STEP_TASK = "ON_STEP_TASK";
@Autowired
PlatformTransactionManager transactionManager;
@Bean(name = "stepOn01")
public Step stepOn01(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("stepOn01", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 01 Tasklet ...");
Random random = new Random();
int randomValue = random.nextInt(1000);
if (randomValue % 2 == 0) {
return RepeatStatus.FINISHED;
} else {
throw new RuntimeException("Error This value is Odd: " + randomValue);
}
}
}, transactionManager)
.build();
}
@Bean(name = "stepOn02")
public Step stepOn02(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("stepOn02", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 02 Tasklet ...");
return RepeatStatus.FINISHED;
}
}, transactionManager)
.build();
}
@Bean(name = "stepOn03")
public Step stepOn03(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("stepOn03", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 03 Tasklet ...");
return RepeatStatus.FINISHED;
}
}, transactionManager)
.build();
}
@Bean
public Job onStepJob(Step stepOn01, Step stepOn02, Step stepOn03, JobRepository jobRepository) {
log.info("------------------ Init myJob -----------------");
return new JobBuilder(ON_STEP_TASK, jobRepository)
.incrementer(new RunIdIncrementer())
.start(stepOn01)
.on("FAILED").to(stepOn03)
.from(stepOn01).on("COMPLETED").to(stepOn02)
.end()
.build();
}
}
실행 결과
짝수 나오는 경우
2024-12-16T22:15:57.818+09:00 INFO 40216 --- [ main] c.k.s.b.j.m.MybatisItemWriterJobConfig : ------------------ Init flatFileStep -----------------
2024-12-16T22:15:57.843+09:00 INFO 40216 --- [ main] c.k.s.b.j.m.MybatisItemWriterJobConfig : ------------------ Init flatFileJob -----------------
2024-12-16T22:15:57.849+09:00 INFO 40216 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:15:57.851+09:00 INFO 40216 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:15:57.852+09:00 INFO 40216 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:15:57.853+09:00 INFO 40216 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:15:57.854+09:00 INFO 40216 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:15:57.854+09:00 INFO 40216 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:15:57.856+09:00 INFO 40216 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:15:58.002+09:00 INFO 40216 --- [ main] c.k.s.b.BatchSampleApplication : Started BatchSampleApplication in 2.596 seconds (process running for 3.114)
2024-12-16T22:15:58.005+09:00 INFO 40216 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2024-12-16T22:15:58.329+09:00 INFO 40216 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=ON_STEP_TASK]] launched with the following parameters: [{'run.id':'{value=2, type=class java.lang.Long, identifying=true}'}]
2024-12-16T22:15:58.505+09:00 INFO 40216 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn01]
2024-12-16T22:15:58.570+09:00 INFO 40216 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 01 Tasklet ...
2024-12-16T22:15:58.623+09:00 INFO 40216 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn01] executed in 117ms
2024-12-16T22:15:58.816+09:00 INFO 40216 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn02]
2024-12-16T22:15:58.868+09:00 INFO 40216 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 02 Tasklet ...
2024-12-16T22:15:58.919+09:00 INFO 40216 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn02] executed in 103ms
2024-12-16T22:15:59.023+09:00 INFO 40216 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=ON_STEP_TASK]] completed with the following parameters: [{'run.id':'{value=2, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 637ms
2024-12-16T22:15:59.031+09:00 INFO 40216 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-12-16T22:15:59.034+09:00 INFO 40216 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-12-16T22:16:00.942+09:00 INFO 40216 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
홀수 나오는 경우
2024-12-16T22:25:35.497+09:00 INFO 41688 --- [ main] c.k.s.b.j.m.MybatisItemWriterJobConfig : ------------------ Init flatFileStep -----------------
2024-12-16T22:25:35.520+09:00 INFO 41688 --- [ main] c.k.s.b.j.m.MybatisItemWriterJobConfig : ------------------ Init flatFileJob -----------------
2024-12-16T22:25:35.525+09:00 INFO 41688 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:25:35.526+09:00 INFO 41688 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:25:35.527+09:00 INFO 41688 --- [ main] c.k.s.b.j.NextStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:25:35.528+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:25:35.529+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:25:35.529+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:25:35.530+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:25:35.672+09:00 INFO 41688 --- [ main] c.k.s.b.BatchSampleApplication : Started BatchSampleApplication in 2.587 seconds (process running for 3.05)
2024-12-16T22:25:35.675+09:00 INFO 41688 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2024-12-16T22:25:35.943+09:00 INFO 41688 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=ON_STEP_TASK]] launched with the following parameters: [{'run.id':'{value=6, type=class java.lang.Long, identifying=true}'}]
2024-12-16T22:25:36.127+09:00 INFO 41688 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn01]
2024-12-16T22:25:36.188+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 01 Tasklet ...
2024-12-16T22:25:36.189+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Random value is 845
2024-12-16T22:25:36.189+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Random value is odd
2024-12-16T22:25:36.199+09:00 ERROR 41688 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step stepOn01 in job ON_STEP_TASK
java.lang.RuntimeException: Error This value is Odd: 845
at com.ksko.spring_batch.batch_sample.jobs.OnStepTaskJobConfiguration$1.execute(OnStepTaskJobConfiguration.java:49) ~[main/:na]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:388) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:312) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-6.1.12.jar:6.1.12]
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:255) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:369) ~[spring-batch-infrastructure-5.1.2.jar:5.1.2]
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:206) ~[spring-batch-infrastructure-5.1.2.jar:5.1.2]
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:140) ~[spring-batch-infrastructure-5.1.2.jar:5.1.2]
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:240) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:229) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:68) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:68) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:165) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:140) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:132) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:317) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:157) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:148) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:59) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:210) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:194) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:174) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:169) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:164) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.lambda$callRunner$4(SpringApplication.java:786) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:786) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.3.3.jar:3.3.3]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:342) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.3.jar:3.3.3]
at com.ksko.spring_batch.batch_sample.BatchSampleApplication.main(BatchSampleApplication.java:12) ~[main/:na]
2024-12-16T22:25:36.231+09:00 INFO 41688 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn01] executed in 102ms
2024-12-16T22:25:36.443+09:00 INFO 41688 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn03]
2024-12-16T22:25:36.500+09:00 INFO 41688 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 03 Tasklet ...
2024-12-16T22:25:36.545+09:00 INFO 41688 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn03] executed in 102ms
2024-12-16T22:25:36.636+09:00 INFO 41688 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=ON_STEP_TASK]] completed with the following parameters: [{'run.id':'{value=6, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 646ms
2024-12-16T22:25:36.642+09:00 INFO 41688 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-12-16T22:25:36.644+09:00 INFO 41688 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-12-16T22:25:38.713+09:00 INFO 41688 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
홀수나 나오면 exception 뜨는것 확인 할 수 있다.
stop
- 특정 step의 작업 결과의 상태를 보고 정지할지 결정
- step1의 결과가 실패인 경우 stop 을 통해 배치작업을 정지한다. (위 2번째 예제는 실패해도 배치는 실행이 끝까지 됨)
StopStepTaskJobConfiguration.java
package com.ksko.spring_batch.batch_sample.jobs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.scope.context.ChunkContext;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import java.util.Random;
@Slf4j
@Configuration
public class StopStepTaskJobConfiguration {
public static final String STOP_STEP_TASK = "STOP_STEP_TASK";
@Autowired
PlatformTransactionManager transactionManager;
@Bean(name = "stepStop01")
public Step stepStop01(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("stepStop01", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 01 Tasklet ...");
Random random = new Random();
int randomValue = random.nextInt(1000);
if (randomValue % 2 == 0) {
return RepeatStatus.FINISHED;
} else {
throw new RuntimeException("Error This value is Odd: " + randomValue);
}
}
}, transactionManager)
.build();
}
@Bean(name = "stepStop02")
public Step stepStop02(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
log.info("------------------ Init myStep -----------------");
return new StepBuilder("stepStop02", jobRepository)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Execute Step 02 Tasklet ...");
return RepeatStatus.FINISHED;
}
}, transactionManager)
.build();
}
@Bean
public Job stopStepJob(Step stepOn01, Step stepOn02, Step stepOn03, JobRepository jobRepository) {
log.info("------------------ Init myJob -----------------");
return new JobBuilder(STOP_STEP_TASK, jobRepository)
.incrementer(new RunIdIncrementer())
.start(stepOn01)
.on("FAILED").stop()
.from(stepOn01).on("COMPLETED").to(stepOn02)
.end()
.build();
}
}
짝수일 경우
2024-12-16T22:45:33.472+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:45:33.490+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:45:33.491+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:45:33.493+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:45:33.504+09:00 INFO 29036 --- [ main] c.k.s.b.j.StopStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:45:33.505+09:00 INFO 29036 --- [ main] c.k.s.b.j.StopStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:45:33.506+09:00 INFO 29036 --- [ main] c.k.s.b.j.StopStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:45:33.643+09:00 INFO 29036 --- [ main] c.k.s.b.BatchSampleApplication : Started BatchSampleApplication in 2.546 seconds (process running for 3.03)
2024-12-16T22:45:33.646+09:00 INFO 29036 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2024-12-16T22:45:34.048+09:00 INFO 29036 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=STOP_STEP_TASK]] launched with the following parameters: [{'run.id':'{value=8, type=class java.lang.Long, identifying=true}'}]
2024-12-16T22:45:34.254+09:00 INFO 29036 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn01]
2024-12-16T22:45:34.323+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 01 Tasklet ...
2024-12-16T22:45:34.323+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Random value is 650
2024-12-16T22:45:34.323+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Random value is even
2024-12-16T22:45:34.392+09:00 INFO 29036 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn01] executed in 136ms
2024-12-16T22:45:34.599+09:00 INFO 29036 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn02]
2024-12-16T22:45:34.690+09:00 INFO 29036 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 02 Tasklet ...
2024-12-16T22:45:34.741+09:00 INFO 29036 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn02] executed in 142ms
2024-12-16T22:45:34.858+09:00 INFO 29036 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=STOP_STEP_TASK]] completed with the following parameters: [{'run.id':'{value=8, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 757ms
2024-12-16T22:45:34.862+09:00 INFO 29036 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-12-16T22:45:34.864+09:00 INFO 29036 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-12-16T22:45:36.613+09:00 INFO 29036 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
정상이군..
홀수이면 stepon1 에서 2로 못들어가고 바로 종료된것을 확인할 수 있다
2024-12-16T22:46:53.506+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:46:53.524+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:46:53.525+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:46:53.528+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:46:53.538+09:00 INFO 26336 --- [ main] c.k.s.b.j.StopStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:46:53.539+09:00 INFO 26336 --- [ main] c.k.s.b.j.StopStepTaskJobConfiguration : ------------------ Init myStep -----------------
2024-12-16T22:46:53.541+09:00 INFO 26336 --- [ main] c.k.s.b.j.StopStepTaskJobConfiguration : ------------------ Init myJob -----------------
2024-12-16T22:46:53.669+09:00 INFO 26336 --- [ main] c.k.s.b.BatchSampleApplication : Started BatchSampleApplication in 2.444 seconds (process running for 2.93)
2024-12-16T22:46:53.671+09:00 INFO 26336 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2024-12-16T22:46:53.990+09:00 INFO 26336 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=STOP_STEP_TASK]] launched with the following parameters: [{'run.id':'{value=10, type=class java.lang.Long, identifying=true}'}]
2024-12-16T22:46:54.229+09:00 INFO 26336 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [stepOn01]
2024-12-16T22:46:54.301+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Execute Step 01 Tasklet ...
2024-12-16T22:46:54.301+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Random value is 727
2024-12-16T22:46:54.301+09:00 INFO 26336 --- [ main] c.k.s.b.jobs.OnStepTaskJobConfiguration : Random value is odd
2024-12-16T22:46:54.312+09:00 ERROR 26336 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step stepOn01 in job STOP_STEP_TASK
java.lang.RuntimeException: Error This value is Odd: 727
at com.ksko.spring_batch.batch_sample.jobs.OnStepTaskJobConfiguration$1.execute(OnStepTaskJobConfiguration.java:49) ~[main/:na]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:388) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:312) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-6.1.12.jar:6.1.12]
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:255) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:369) ~[spring-batch-infrastructure-5.1.2.jar:5.1.2]
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:206) ~[spring-batch-infrastructure-5.1.2.jar:5.1.2]
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:140) ~[spring-batch-infrastructure-5.1.2.jar:5.1.2]
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:240) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:229) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:68) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:68) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:165) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:140) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:132) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:317) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:157) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:148) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:59) ~[spring-batch-core-5.1.2.jar:5.1.2]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:210) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:194) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:174) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:169) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:164) ~[spring-boot-autoconfigure-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.lambda$callRunner$4(SpringApplication.java:786) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.12.jar:6.1.12]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:786) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.3.3.jar:3.3.3]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:342) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.3.jar:3.3.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.3.jar:3.3.3]
at com.ksko.spring_batch.batch_sample.BatchSampleApplication.main(BatchSampleApplication.java:12) ~[main/:na]
2024-12-16T22:46:54.342+09:00 INFO 26336 --- [ main] o.s.batch.core.step.AbstractStep : Step: [stepOn01] executed in 112ms
2024-12-16T22:46:54.441+09:00 INFO 26336 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=STOP_STEP_TASK]] completed with the following parameters: [{'run.id':'{value=10, type=class java.lang.Long, identifying=true}'}] and the following status: [STOPPED] in 404ms
2024-12-16T22:46:54.446+09:00 INFO 26336 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-12-16T22:46:54.448+09:00 INFO 26336 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-12-16T22:46:56.168+09:00 INFO 26336 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
끝났다.. ㅠㅠ 다시 복습 꼭 해야겟다 ㅠㅠ
그리고 11주차도 하자..
728x90