기본 개념
- 프레임워크에서 유지 및 관리하는 “키 / 값”으로 된 컬렉션으로 StepExecution 또는 JobExecution 객체의 상태(state)를 저장하는 공유 객체
- DB 에 직렬화 한 값으로 저장됨 {”key” : “value”}
- 공유 범위
- Step 범위 = 각 Step의 StepExecution에 저장되며 Step 간 서로 공유 안됨
- Job 범위 = 각 Job의 JobExecution에 저장되며 Job 간 서로 공유 안되며 해당 Job의 Step 간 서로 공유됨
- Job 재 시작 시 이미 처리한 Row 데이터는 건너뛰고 이후로 수행하도록 할 때 상태 정보를 활용한다.
구조
ExecutionContext
Map<String , Object> map = new ConcurrentHashMap
- 유지 , 관리에 필요한 키값 설정
그림으로 이해하기
위 그림이 저장될 때👇
BATCH_JOB_EXECUTION_CONTEXT
job_execution_id | short_context | serialized_context |
1 | { "inputCount" : "150" , "date" : "20220819"} |
BATCH_STEP_EXECUTION_CONTEXT
step_execution_id | short_context | serialized_context |
1 | {”batch.taskletType”:”io.batch.springbatch.job.HellojobConfiguation$1”, ”name”:”leaven”, ”batch.stepType”:”org.springframework.batch.core.step.tasklet.TaskletStep”} |
코드로 보기
JobConfiguration
package io.batch;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
@RequiredArgsConstructor
public class DBJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final ExecutionContextTasklet1 executionContextTasklet1;
private final ExecutionContextTasklet2 executionContextTasklet2;
private final ExecutionContextTasklet3 executionContextTasklet3;
private final ExecutionContextTasklet4 executionContextTasklet4;
@Bean
public Job job(){
return jobBuilderFactory.get("job")
.start(step1())
.next(step2())
.next(step3())
.next(step4())
.build();//build를 해주면 실제 Job 생성
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(executionContextTasklet1)
.build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2")
.tasklet(executionContextTasklet2)
.build();
}
@Bean
public Step step3() {
return stepBuilderFactory.get("step3")
.tasklet(executionContextTasklet3)
.build();
}
@Bean
public Step step4() {
return stepBuilderFactory.get("step4")
.tasklet(executionContextTasklet4)
.build();
}
}
- 4개의 Step 가 돌아간다
- 3번째 Tasklet에서 실패하게 코드를 작성
- 결과를 확인해보자
ExecutionContextTasklet1
package io.batch;
import lombok.extern.log4j.Log4j2;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Log4j2
@Component
public class ExecutionContextTasklet1 implements Tasklet {
//StepContribution 클래스 안에있는 StepExecution 을 참조해서 ExecutionContext 을 활용하는 방법을 사용하고자한다
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
log.info("step1 돌아간다아~");
ExecutionContext jobExecutionContext = stepContribution.getStepExecution().getJobExecution().getExecutionContext();
ExecutionContext stepExecutionContext = stepContribution.getStepExecution().getExecutionContext();
//getJobName 꺼내기
String jobName = chunkContext.getStepContext().getStepExecution().getJobExecution().getJobInstance().getJobName();
//getStepName 꺼내기
String stepName = chunkContext.getStepContext().getStepExecution().getStepName();
//jobName 가 null 이라는건 jobExecutionContext 안에 jobName 키로하는 값을 저장한 적이 없는 경우
if(jobExecutionContext.get("jobName") == null){
jobExecutionContext.put("jobName",jobName);
}
if(stepExecutionContext.get("stepName") == null){
stepExecutionContext.put("stepName",stepName);
}
log.info("jobName : " + jobExecutionContext.get("jobName"));
log.info("stepName :" + jobExecutionContext.get("stepName"));
return RepeatStatus.FINISHED;
}
}
ExecutionContextTasklet2
package io.batch;
import lombok.extern.log4j.Log4j2;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Component
@Log4j2
public class ExecutionContextTasklet2 implements Tasklet {
//StepContribution 클래스 안에있는 StepExecution 을 참조해서 ExecutionContext 을 활용하는 방법을 사용하고자한다
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
log.info("step1 이 성공했구나? ");
log.info("step2 돌아간다아~");
ExecutionContext jobExecutionContext = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
ExecutionContext stepExecutionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();
log.info("jobName : " + jobExecutionContext.get("jobName")); //step1 이 성공했다면 jobExecutionContext 에서는 step1 에서 입력된 값이 보일것
log.info("stepName : " + stepExecutionContext.get("stepName")); //step1 이 성공했어도 step 끼리는 공유가 안되기때문에 값이 없다
String stepName = chunkContext.getStepContext().getStepExecution().getStepName();
if(stepExecutionContext.get("stepName") == null){
stepExecutionContext.put("stepName",stepName);
}
return RepeatStatus.FINISHED;
}
}
ExecutionContextTasklet3
package io.batch;
import lombok.extern.log4j.Log4j2;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Component
@Log4j2
public class ExecutionContextTasklet3 implements Tasklet {
//StepContribution 클래스 안에있는 StepExecution 을 참조해서 ExecutionContext 을 활용하는 방법을 사용하고자한다
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
log.info("step3 실패 예외발생~");
Object name = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().get("name");
if(name == null){
chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("name","user1");
throw new RuntimeException("step2 was failed");
}
return RepeatStatus.FINISHED;
}
}
ExecutionContextTasklet4
package io.batch;
import lombok.extern.log4j.Log4j2;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Component
@Log4j2
public class ExecutionContextTasklet4 implements Tasklet {
//StepContribution 클래스 안에있는 StepExecution 을 참조해서 ExecutionContext 을 활용하는 방법을 사용하고자한다
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
log.info("step3 에서 저장한 name ");
log.info("name : " + chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().get("name"));
log.info("step4 돌아간다아~");
return RepeatStatus.FINISHED;
}
}
실행 ~~
batch_step_execution 테이블
- 테이블은 보면 총 2번 실행했고 첫 번째 실행 때는 정상적으로 진행된 step 1 , 2 그다음 3번에서 실패해서 거기서 종료되고 한 번 더 실행헀을때는 성공했던 step1 , 2를 제외하고 3 , 4 가 실행된 걸 볼 수 있다
'Spring Batch' 카테고리의 다른 글
Spring Batch 도메인 StepContribution (0) | 2022.08.30 |
---|---|
Spring Batch 도메인 StepExecution (0) | 2022.08.24 |
Spring Batch 도메인 Step (0) | 2022.08.23 |
Spring Batch 도메인 JobExecution (0) | 2022.08.22 |
Spring Batch 도메인 JobParameters (0) | 2022.08.19 |