CompositeItemWriter 예제 - 구현

2018. 12. 2. 23:17Spring Batch

반응형

CompositeItemWriter 구현

20건의 CSV 파일에서 데이터를 읽어서 MySQL, Oracle 데이터베이스에 적재하도록 구현.

테스트는 정상인 경우 오류인 경우 트랜잭션에 대한 동작 여부 확인



CompositItemWriterJob.xml 작성


▩ Job ID 설정

<job id="compositeItemWriterJob" xmlns="http://www.springframework.org/schema/batch">
	<step id="compositeWriterStep">
		<tasklet>
			<chunk reader="compositeReader"
				writer="compositeWriter"
				commit-interval="100"/>
		</tasklet>
	</step>
</job>

▩ Reader 설정 - compositeReader

<bean id="compositeReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
	<property name="resource" value="file:{파일경로}/fifa_ranking_delimiter_1.csv" />
	
	<property name="lineMapper">
		<bean class="egovframework.rte.bat.core.item.file.mapping.EgovDefaultLineMapper">
			<property name="lineTokenizer">
				<bean class="egovframework.rte.bat.core.item.file.transform.EgovDelimitedLineTokenizer">
					<property name="delimiter" value=","/>
				</bean>
			</property>
			<property name="objectMapper">
				<bean class="egovframework.rte.bat.core.item.file.mapping.EgovObjectMapper">
					<property name="type" value="com.batchguide.dto.FifaRankingDTO" />
					<property name="names" value="rank,country_full,country_abrv,total_points,previous_points,rank_change,cur_year_avg,
						cur_year_avg_weighted,last_year_avg,last_year_avg_weighted,two_year_ago_avg,two_year_ago_weighted,three_year_ago_avg,
						three_year_ago_weighted,confederation,rank_date" />
				</bean>
			</property>
		</bean>
	</property>
</bean>


▩ Writer 설정 - compositeWriter

<bean id="compositeWriter" class="org.springframework.batch.item.support.CompositeItemWriter">
	<property name="delegates">
		<list>
			<ref bean="mysqlDbWriter" /> 
			<ref bean="oracleDbWriter" />
		</list>
	</property>
</bean>


▩ Writer Reference Bean 설정 - mysqlDbWriter

<bean id="mysqlDbWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter">
	<property name="statementId" value="insertFifaRanking" />
	<property name="sqlSessionTemplate" ref="batchMysqlSqlSessionTemplate" />
</bean>


▩ Writer Reference Bean 설정 - oracleDbWriter

<bean id="oracleDbWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter">
	<property name="statementId" value="insertFifaRanking1" />
	<property name="sqlSessionTemplate" ref="batchOracleSqlSessionTemplate" />
</bean>	


▩ OracleQuerySQL.xml - insertFifaRanking1 등록 쿼리 설정

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
	
<mapper namespace="oracleQuerySQL">
	<insert id="insertFifaRanking1" parameterType="com.batchguide.dto.FifaRankingDTO">
		INSERT /* id:insertFifaRanking1| Fifa 순위 저장 | DbToDbSQL.xml */ 
		  INTO FIFA_RANKING
				 (RNK
				, RNK_DATE
				, COUNTRY_FULL
				, COUNTRY_ABRV
				, TOTAL_POINTS
				, PREVIOUS_POINTS
				, RANK_CHANGE
				, CUR_YEAR_AVG
				, CUR_YEAR_AVG_WEIGHTED
				, LAST_YEAR_AVG
				, LAST_YEAR_AVG_WEIGHTED
				, TWO_YEAR_AGO_AVG
				, TWO_YEAR_AGO_WEIGHTED
				, THREE_YEAR_AGO_AVG
				, THREE_YEAR_AGO_WEIGHTED
				, CONFEDERATION)
	   VALUES (#{rank}
				, #{rank_date}
				, #{country_full}
				, #{country_abrv}
				, #{total_points}
				, #{previous_points}
				, #{rank_change}
				, #{cur_year_avg}
				, #{cur_year_avg_weighted}
				, #{last_year_avg}
				, #{last_year_avg_weighted} 
				, #{two_year_ago_avg}
				, #{two_year_ago_weighted}
				, #{three_year_ago_avg}
				, #{three_year_ago_weighted}
				, #{confederation})
	</insert>
</mapper>



CompositItemWriterJob.xml 전체


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/batch
						http://www.springframework.org/schema/batch/spring-batch.xsd">
						
	<description>
	<![CDATA[예제는 파일데이터를 읽어서 MySQL, Oracle DB에 적재하는 예제이다.
		Reader는 FlatItemReader를 사용하며, Writer는 CompositeItemWriter를 사용하여서
		처리한다.
	]]>
	</description>
	
	<job id="compositeItemWriterJob" xmlns="http://www.springframework.org/schema/batch">
		<step id="compositeWriterStep">
			<tasklet>
				<chunk reader="compositeReader"
					writer="compositeWriter"
					commit-interval="100"/>
			</tasklet>
		</step>
	</job>
	
	<!-- Reader 설정 -->
	<bean id="compositeReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
		<property name="resource" value="file:C:/Users/pncrm/git/spring_batch_guide/spring_batch_guide/src/main/resources/config/batch/data/inputs/fifa_ranking_delimiter_1.csv" />
		
		<property name="lineMapper">
			<bean class="egovframework.rte.bat.core.item.file.mapping.EgovDefaultLineMapper">
				<property name="lineTokenizer">
					<bean class="egovframework.rte.bat.core.item.file.transform.EgovDelimitedLineTokenizer">
						<property name="delimiter" value=","/>
					</bean>
				</property>
				<property name="objectMapper">
					<bean class="egovframework.rte.bat.core.item.file.mapping.EgovObjectMapper">
						<property name="type" value="com.batchguide.dto.FifaRankingDTO" />
						<property name="names" value="rank,country_full,country_abrv,total_points,previous_points,rank_change,cur_year_avg,
							cur_year_avg_weighted,last_year_avg,last_year_avg_weighted,two_year_ago_avg,two_year_ago_weighted,three_year_ago_avg,
							three_year_ago_weighted,confederation,rank_date" />
					</bean>
				</property>
			</bean>
		</property>
	</bean>
	
	<!-- CompositeWriter 설정 -->
	<bean id="compositeWriter" class="org.springframework.batch.item.support.CompositeItemWriter">
		<property name="delegates">
			<list>
				<ref bean="mysqlDbWriter" /> 
				<ref bean="oracleDbWriter" />
			</list>
		</property>
	</bean>
	
	
	<!-- MySQL Writer 설정 -->
	<bean id="mysqlDbWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter">
		<property name="statementId" value="insertFifaRanking" />
		<property name="sqlSessionTemplate" ref="batchMysqlSqlSessionTemplate" />
	</bean>
	
	<!-- Oracle Writer 설정 -->
	<bean id="oracleDbWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter">
		<property name="statementId" value="insertFifaRanking1" />
		<property name="sqlSessionTemplate" ref="batchOracleSqlSessionTemplate" />
	</bean>	
	
</beans>


테스트결과


테스트데이터가 20건이 되지 않으므로 commit-interval=10으로 설정 하여서 테스트 한다.


▩ 정상실행결과


▩ 실패실행결과

[DB조회결과]

- 처번째 10건 조회한 데이터 입력시 오류가 발생하므로 READ_COUNT 는 10으로 조회된다.


[콘솔실행결과]

2018-12-02 22:57:53,194  INFO [com.batchguide.CommandLineJobRunner] ###################### Command Line Job Runner Start ######################
Parameter[0] : TIMESTAMP=1543759073230
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
2018-12-02 22:58:01,255 ERROR [org.springframework.batch.core.step.AbstractStep] Encountered an error executing step compositeWriterStep in job compositeItemWriterJob
org.springframework.jdbc.UncategorizedSQLException: oracleQuerySQL.insertFifaRanking1 (batch index #1) failed. Cause: java.sql.BatchUpdateException: ORA-12899: "SYSTEM"."FIFA_RANKING"."CONFEDERATION" 열에 대한 값이 너무 큼(실제: 8, 최대값: 5)

; uncategorized SQLException; SQL state [72000]; error code [12899]; ORA-12899: "SYSTEM"."FIFA_RANKING"."CONFEDERATION" 열에 대한 값이 너무 큼(실제: 8, 최대값: 5)
; nested exception is java.sql.BatchUpdateException: ORA-12899: "SYSTEM"."FIFA_RANKING"."CONFEDERATION" 열에 대한 값이 너무 큼(실제: 8, 최대값: 5)

	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:89) ~[spring-jdbc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73) ~[mybatis-spring-1.3.1.jar:1.3.1]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446) ~[mybatis-spring-1.3.1.jar:1.3.1]
	at com.sun.proxy.$Proxy26.flushStatements(Unknown Source) ~[?:?]
	at org.mybatis.spring.SqlSessionTemplate.flushStatements(SqlSessionTemplate.java:394) ~[mybatis-spring-1.3.1.jar:1.3.1]
	at org.mybatis.spring.batch.MyBatisBatchItemWriter.write(MyBatisBatchItemWriter.java:131) ~[mybatis-spring-1.3.1.jar:1.3.1]
	at org.springframework.batch.item.support.CompositeItemWriter.write(CompositeItemWriter.java:59) ~[spring-batch-infrastructure-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:185) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:151) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.item.SimpleChunkProcessor.write(SimpleChunkProcessor.java:284) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:209) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.0.8.RELEASE.jar:5.0.8.RELEASE]
	at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:272) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257) ~[spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:66) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:136) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:308) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:141) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-5.0.8.RELEASE.jar:5.0.8.RELEASE]
	at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:134) [spring-batch-core-4.0.1.RELEASE.jar:4.0.1.RELEASE]
	at egovframework.rte.bat.core.launch.support.EgovCommandLineRunner.start(EgovCommandLineRunner.java:287) [egovframework.rte.bat.core-3.7.0.jar:?]
	at com.batchguide.util.RunBatch.start(RunBatch.java:78) [classes/:?]
	at com.batchguide.CommandLineJobRunner.main(CommandLineJobRunner.java:32) [classes/:?]
Caused by: java.sql.BatchUpdateException: ORA-12899: "SYSTEM"."FIFA_RANKING"."CONFEDERATION" 열에 대한 값이 너무 큼(실제: 8, 최대값: 5)

	at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:11190) ~[ojdbc7-12.1.0.jar:12.1.0.1.0]
	at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:244) ~[ojdbc7-12.1.0.jar:12.1.0.1.0]
	at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) ~[commons-dbcp-1.4.jar:1.4]
	at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) ~[commons-dbcp-1.4.jar:1.4]
	at org.apache.ibatis.executor.BatchExecutor.doFlushStatements(BatchExecutor.java:122) ~[mybatis-3.4.1.jar:3.4.1]
	at org.apache.ibatis.executor.BaseExecutor.flushStatements(BaseExecutor.java:129) ~[mybatis-3.4.1.jar:3.4.1]
	at org.apache.ibatis.executor.BaseExecutor.flushStatements(BaseExecutor.java:122) ~[mybatis-3.4.1.jar:3.4.1]
	at org.apache.ibatis.executor.CachingExecutor.flushStatements(CachingExecutor.java:114) ~[mybatis-3.4.1.jar:3.4.1]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.flushStatements(DefaultSqlSession.java:253) ~[mybatis-3.4.1.jar:3.4.1]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_152]
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_152]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_152]
	at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_152]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433) ~[mybatis-spring-1.3.1.jar:1.3.1]
	... 32 more
2018-12-02 22:58:01,328  INFO [com.batchguide.util.RunBatch] Status Code: 1
2018-12-02 22:58:01,328 ERROR [com.batchguide.util.RunBatch] compositeItemWriterJob 실패 [FAIL]
2018-12-02 22:58:01,328 ERROR [com.batchguide.util.RunBatch] Error Message: 
2018-12-02 22:58:01,328  INFO [com.batchguide.CommandLineJobRunner] ###################### Command Line Job Runner End   ######################



[소스레파지토리]

☞ https://github.com/roopy1210/springbatch/blob/master/spring_batch_tutorial


반응형

'Spring Batch' 카테고리의 다른 글

Range Partitioning 예제 - 구현  (0) 2018.12.18
Range Partitioning 예제 - 설정  (0) 2018.12.17
CompositeItemWriter 예제 - 설정  (0) 2018.12.02
DB To File 예제  (0) 2018.11.14
MULTI FILE TO DB - 다중파일 예제  (0) 2018.11.01