개발/Spring Batch

step을 loop 구조로 수행하기, step과 step간 파라미터 넘기기

신매력 2016. 6. 22. 18:39

보통은 한 step 내에서 Reader, Writer 구조로 잡고, reader 내에서 페이징을 하지만

아래 예제의 경우 한 step을 Reader처럼 두고, loop를 돌면서 Step 간의 페이징을 해볼 것이다.

 

 

* 흐름에 대한 대략적인 정리라 컴파일은 따로 안해봤음

 

 

1. job.xml 설정

<batch:job id="loopStepTestJob">
	<batch:step id="readerStep" next="limitDecision">
		<batch:tasklet ref="testItemReaderTasklet" />
	</batch:step>


 	<batch:decision id="limitDecision" decider="limitDecider">
 		<batch:next on="CONTINUE" to="readerStep" />
 		<batch:end on="COMPLETED" />
 	</batch:decision> 
</batch:job>


<bean id="testItemReaderTasklet" class="com.job.test.TestItemReaderTasklet" scope="step">
	<property name="bandPageSize" value="#{jobParameters['pageSize']}" />
</bean>


<bean id="limitDecider" class="com.job.test.LimitDecider"/>

 

 

2. Reader Tasklet

public class TestItemReaderTaskletimplements Tasklet {

	@Autowired private testDAO testDAO;

	private int pageSize;
	// TODO getter, setter


 	@Override
 	public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
		// 페이징을 위한 변수. step이 loop를 돌아올 때 값이 채워져있음
		Integer lastId = (Integer) chunkContext.getStepContext().getJobExecutionContext().get("lastId");

 		List<Map<String, Object>> results = testDAO.selectData(lastId);


		if (CollectionUtils.isEmpty(results) == false) {
			lastId = (Integer) results.get(results.size()-1).get("id");
		}

		// 페이징을 위한 변수 값을 jobExecution에 저장해둠
 		chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("lastId", lastId);
		return RepeatStatus.FINISHED;
	}
}

 

 

3. test.xml 쿼리

<select id="selectData" parameterType="integer" resultType="hashmap">
 SELECT
 *
 FROM test
 WHERE 
  status = 0
 <if test="lastId != null">
 AND id > #{lastId}
 </if>
 ORDER BY id
 LIMIT 100
</select>

 

4. LimitDecider (loop 여부 결정)

public class LimitDecider implements JobExecutionDecider {
	@Override
	public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) { 
		// reader step에서 저장했던 값을 꺼낸다
		ExecutionContext jobContext = jobExecution.getExecutionContext();
		Integer lastId = (Integer) jobContext.get("lastId");


		if (lastId == null) {
			return new FlowExecutionStatus("COMPLETED");
		} else {
			return new FlowExecutionStatus("CONTINUE"); 
		}
	}
}

 

step 과 step 간에 파라미터를 넘기려면 jobExecution 말고 stepExecution으로 사용해도 되는데,

이 예제의 경우 스텝간 loop 구조이고, 

계속 들고 값 유지를 해야하기 때문에 job단위의 파라미터로 저장해 놓았다.

 

 

 

* 참고

 

나는 loop step + 파티셔닝을 짬뽕한 구조로 구현했는데,

(reader step - decider - partitioning) 

 

파티션 클래스에서는 JobExecution, StepExecution을 파라미터로 받아오지 않는다. 

 

그런 경우에는 jobExecution을 사용하려는 클래스에서 아래와 같이 사용하면 된다.

 

@Value("#{jobExecutionContext['lastId']}")
int lastId;