보통은 한 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;
'개발 > Spring Batch' 카테고리의 다른 글
Spring batch step loop 할 때 메모리 이슈 (0) | 2016.07.22 |
---|---|
Spring Batch 파티셔닝(partitioning) 예제(Database range query) (0) | 2016.05.25 |
Spring Batch decision으로 step 결정하기 (1) | 2015.10.29 |
Spirng Batch 로 Batch Job 만들기 (2) (0) | 2012.08.17 |
Spirng Batch 로 Batch Job 만들기 (1) (0) | 2012.08.16 |