배치 수행 중, 데이터를 처리하는 과정에서 에러가 발생한 데이터를 건너뛰고 다음 데이터를 처리하는 기능을 보여주는 예제이다. EgovSkipSampleFunctionalTests 예제는 ItemReading,ItemProcessing, ItemWriting 에서 발생한 예외상황에 대해 정해진 설정대로 Skip을 수행되는 과정을 보여준다.
✔ skipSample의 Step 흐름 참고
Job 의 구성을 보면 Chunk 설정에 아래와 같은 설정이 있다.
<include> : skip 해야하는 Exception 지정 <exclude> : include로 지정한 exception의 하위 exception 중, skip 하지 않을 Exception 지정
<job id="skipJob" incrementer="incrementer" xmlns="http://www.springframework.org/schema/batch"> <step id="step1" parent="baseStep"> <tasklet> <chunk reader="fileItemReader" processor="tradeProcessor" writer="tradeWriter" commit-interval="3" skip-limit="10"> <skippable-exception-classes> <include class="org.springframework.batch.item.file.FlatFileParseException" /> <include class="org.springframework.batch.item.WriteFailedException" /> </skippable-exception-classes> </chunk> </tasklet> <next on="*" to="step2" /> <next on="COMPLETED WITH SKIPS" to="errorPrint1" /> <fail on="FAILED" exit-code="FAILED" /> </step> <step id="errorPrint1" next="step2"> <tasklet ref="errorLogTasklet" /> </step> <step id="step2" parent="baseStep" next="skipCheckingDecision"> <tasklet> <chunk reader="tradeSqlItemReader" processor="tradeProcessorFailure" writer="itemTrackingWriter" commit-interval="2" skip-limit="10"> <skippable-exception-classes merge="true"> <include class="org.springframework.batch.item.validator.ValidationException" /> <include class="java.io.IOException" /> </skippable-exception-classes> </chunk> <no-rollback-exception-classes> <include class="org.springframework.batch.item.validator.ValidationException" /> </no-rollback-exception-classes> </tasklet> </step> <decision id="skipCheckingDecision" decider="skipCheckingDecider"> <end on="*" /> <next on="COMPLETED WITH SKIPS" to="errorPrint2" /> <fail on="FAILED" exit-code="FAILED" /> </decision> <step id="errorPrint2"> <tasklet ref="errorLogTasklet" /> </step> </job>
✔ JunitTest 클래스의 구조는 배치실행환경 예제 Junit Test 설명을 참고한다.
✔ assertEquals(“COMPLETED”, jobExecution.getExitStatus().getExitCode()) : 배치수행결과가 COMPLETED 인지 확인한다.
✔ 다음 Tests 클래스의 주석을 참고하여 Step의 흐름에 따라 Skip이 일어나는 과정을 확인한다.
@ContextConfiguration(locations = {"/egovframework/batch/simple-job-launcher-context.xml", "/egovframework/batch/jobs/skipSampleJob.xml", "/egovframework/batch/job-runner-context.xml" }) public class EgovSkipSampleFunctionalTests { ... @Test public void testJobIncrementing() { ... JobExecution execution1 = jobExplorer.getJobExecution(id1); assertEquals(BatchStatus.COMPLETED, execution1.getStatus()); validateLaunchWithSkips(execution1); } private void validateLaunchWithSkips(JobExecution jobExecution) { // Step1: 10 input 레코드, READ 에서 2건 SKIP (ROW:5,10), WRITE 1 건 SKIP => OUTPUT 결과 7건 assertEquals(7, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "TRADE")); // Step2: 7 input 레코드, PROCESS 에서 1건 SKIP, WRITE에서 1건 SKIP => OUTPUT 결과 5건 assertEquals(5, simpleJdbcTemplate.queryForInt("SELECT COUNT(*) from TRADE where VERSION=?", 1)); // 두번째 스텝에서 1건의 SKIP 발생 assertEquals(1, EgovSkipCheckingListener.getProcessSkips()); // 두 스텝 모두 ERROR_LOG 테이블에 정보 찍힘 assertEquals(2, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "ERROR_LOG")); //Step1 에서 3건 SKIP 되었다는 정보 확인 assertEquals("3 records were skipped!", simpleJdbcTemplate.queryForObject( "SELECT MESSAGE from ERROR_LOG where JOB_NAME = ? and STEP_NAME = ?", String.class, "skipJob", "step1")); //Step2 에서 2건 SKIP 되었다는 정보 확인 assertEquals("2 records were skipped!", simpleJdbcTemplate.queryForObject( "SELECT MESSAGE from ERROR_LOG where JOB_NAME = ? and STEP_NAME = ?", String.class, "skipJob", "step2")); } }
수행방법은 JunitTest 실행을 참고한다.