CompositeItemWriter 예제 - 설정

2018. 12. 2. 22:29Spring Batch

반응형

CompositeItemWriter 설정

리소스파일은 하나이고 두종류의 데이터베이스에 정보를 저장한다고 가정 해 본다면 하나의 READER와 두개의 WRITER를 구성하면 될 것이다.

이와 같은 구성을 위해 CompositeItemWriter 사용에 대한 예제이다.

이 예제의 가장 큰 목적은 2 Phase Commit을 구현하는데 있다. 하나의 데이터베이스 저장 시 오류가 난다면 전체 Transaction에 대한 Rollback처리 구현에 목적이 있다.



사전준비

이제 두개의 데이터베이스를 사용해야 하므로 기존에 설정되었던 MySQL 이외에 Oracle DB에 대한 설정이 추가 되어져야 하므로 이와 관련된

설정 파일 수정이 먼저 필요하다. 

그리고 멀티 트랜잭션 관리를 위해 스프링의 ChainedTransactionManager를 사용한다.



프로젝트구조



설정파일변경


위의 프로젝트 구조에 보면 /config/batch, /sqlmap/ 아래 설정 파일에 대한 변경



▩ datasource.properteis

log4jdbc 사용 하는 경우로 해서 설정 파일이 작성 되어져 있다. 만약 log4jdbc를 사용하지 않는다면 첫 라인 주석을 풀고 두번째 라인을 주석 처리한다.

#------------------------------------------------------------------------------
# mySql Database
#------------------------------------------------------------------------------
#spring.datasource.mysql.DriverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.mysql.DriverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.mysql.Url=jdbc:log4jdbc:mysql://localhost:3306/mysql?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Seoul&useSSL=false
spring.datasource.mysql.UserName=xxxx
spring.datasource.mysql.Password=xxxx

#------------------------------------------------------------------------------
# oracle Database
#------------------------------------------------------------------------------
#spring.datasource.oracle.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.oracle.DriverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.oracle.Url=jdbc:log4jdbc:oracle:thin:@localhost:1521:roopy
spring.datasource.oracle.UserName=xxxx
spring.datasource.oracle.Password=xxxx



▩ context-batch-datasource.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"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
						http://www.springframework.org/schema/context
						http://www.springframework.org/schema/context/spring-context-3.0.xsd
						http://www.springframework.org/schema/jdbc
						http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

	<context:annotation-config />
	
	<bean id="egov.propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:/config/batch/properties/datasource.properties</value>
			</list>
		</property>
	</bean>
	
	<bean id="mysqlDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${spring.datasource.mysql.DriverClassName}" />
		<property name="url" value="${spring.datasource.mysql.Url}" />
		<property name="username" value="${spring.datasource.mysql.UserName}" />
		<property name="password" value="${spring.datasource.mysql.Password}" />
		<property name="initialSize" value="2" />
		<property name="maxActive" value="100" />
	</bean>
	
	<bean id="oracleDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${spring.datasource.oracle.DriverClassName}" />
		<property name="url" value="${spring.datasource.oracle.Url}" />
		<property name="username" value="${spring.datasource.oracle.UserName}" />
		<property name="password" value="${spring.datasource.oracle.Password}" />
		<property name="initialSize" value="2" />
		<property name="maxActive" value="100" />
	</bean>
	
</beans>

- MySQL DataSource 작성

- Oracle DataSource 작성



▩ context-batch-job-launcher.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"
	xmlns:p="http://www.springframework.org/schema/p"
	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">
				
	<bean id="eGovBatchRunner" class="egovframework.rte.bat.core.launch.support.EgovBatchRunner">
		<constructor-arg ref="jobOperator" />
		<constructor-arg ref="jobExplorer" />
		<constructor-arg ref="jobRepository" />
	</bean>
	
	<bean id="jobOperator"
		class="org.springframework.batch.core.launch.support.SimpleJobOperator"
		p:jobLauncher-ref="jobLauncher"
		p:jobExplorer-ref="jobExplorer"
		p:jobRepository-ref="jobRepository"
		p:jobRegistry-ref="jobRegistry" />	
		
	<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<property name="jobRepository" ref="jobRepository" />
	</bean>
	
	<bean id="jobRepository"
		class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
		p:dataSource-ref="mysqlDataSource"
		p:transactionManager-ref="transactionManager"
		p:isolationLevelForCreate="ISOLATION_DEFAULT" />
		
	<bean id="jobExplorer"
		class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
		p:dataSource-ref="mysqlDataSource" />
		
	<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
		<property name="jobRegistry" ref="jobRegistry" />
	</bean>	
	
	<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
		
	<!-- MySQL JdbcTemplate -->
	<bean id="mysqlJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="mysqlDataSource" />
	</bean>
	
	<!-- Oracle JdbcTemplate -->
	<bean id="oracleJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="oracleDataSource" />
	</bean>	
						
</beans>

- 스프링배치 메타 테이블은 MySQL에 존재하므로 MySQL데이터소스로 변경

- JdbcTemplate 또한 MySQL과 Oracle두가지로 설정한다.

  JdbcTemplate 은 Tasklet 사용 시 사용한다.



▩ context-batch-mapjob-launcher.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" xmlns:p="http://www.springframework.org/schema/p" 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"> <bean id="eGovBatchRunner" class="egovframework.rte.bat.core.launch.support.EgovBatchRunner"> <constructor-arg ref="jobOperator" /> <constructor-arg ref="jobExplorer" /> <constructor-arg ref="jobRepository" /> </bean> <bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator" p:jobLauncher-ref="jobLauncher" p:jobExplorer-ref="jobExplorer" p:jobRepository-ref="jobRepository" p:jobRegistry-ref="jobRegistry" /> <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> <property name="jobRepository" ref="jobRepository" /> </bean> <!-- JobRepositoryFactoryBean 대신 MapJobRepositoryFactoryBean 적용 --> <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" p:transactionManager-ref="transactionManager" /> <bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean" p:dataSource-ref="mysqlDataSource" /> <bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor"> <property name="jobRegistry" ref="jobRegistry" /> </bean> <bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" /> </beans>



▩ context-batch-transaction.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"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/context
						http://www.springframework.org/schema/context/spring-context.xsd
						http://www.springframework.org/schema/jdbct
						http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

	<context:annotation-config />
	
	<!-- MySQL TransactionManager 설정 -->
	<bean id="mysqlTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
		<property name="dataSource" ref="mysqlDataSource" />
	</bean>
	
	<!-- Oracle TransactionManager 설정 -->
	<bean id="oracleTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
		<property name="dataSource" ref="oracleDataSource" />
	</bean>	
	
	<!-- Chained TransactionManager 설정 -->
	<bean id="transactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
		<constructor-arg>
			<list>
				<ref bean="mysqlTransactionManager"/>
				<ref bean="oracleTransactionManager"/>
			</list>
		</constructor-arg>
	</bean>
	 
</beans>

- 데이터베이스별로 각각 TransactionManager 설정 후 ChainedTransactionManager를 통하여 하나의 Transaction으로 처리 하도록 한다.

- 만약 한쪽의 오류가 발생한다면 두객의 데이터베이스 Rollback 처리 될 것이다.(2 Phase Commit)



▩ context-mapper.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"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
						http://www.springframework.org/schema/batch
						http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">
						
	<!-- MySql myBatis 설정 -->
	<bean id="mysqlSqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="mysqlDataSource" />
		<property name="configLocation" value="classpath:/config/sqlmap/sql-mapper-config.xml" />
		<property name="mapperLocations" value="classpath:/config/sqlmap/mysql/**/*SQL.xml" />
	</bean>
	
	<bean id="mysqlSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="mysqlSqlSession" />
		<constructor-arg index="1" value="SIMPLE" />
	</bean>
	
	<bean id="batchMysqlSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="mysqlSqlSession" />
		<constructor-arg index="1" value="BATCH" />
	</bean>
	
	<!-- Oracle myBatis 설정 -->
	<bean id="oracleSqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="oracleDataSource" />
		<property name="configLocation" value="classpath:/config/sqlmap/sql-mapper-config.xml" />
		<property name="mapperLocations" value="classpath:/config/sqlmap/oracle/**/*SQL.xml" />
	</bean>
	
	<bean id="oracleSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="oracleSqlSession" />
		<constructor-arg index="1" value="SIMPLE" />
	</bean>
	
	<bean id="batchOracleSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="oracleSqlSession" />
		<constructor-arg index="1" value="BATCH" />
	</bean>	
</beans>

- MySQL, Oracle 각각 myBatis 설정을 해준다. 

- SQL 파일 위치는 나누어서 관리한다.



▩ 오라클 FIFA_RANKING 테이블 생성

CREATE TABLE SYSTEM.FIFA_RANKING (
	RNK                        		NUMBER                      NOT NULL, 
	RNK_DATE                  		VARCHAR2(8 BYTE)            NOT NULL, 
	COUNTRY_FULL               		VARCHAR2(50 BYTE)           NULL, 
	COUNTRY_ABRV              		VARCHAR2(10 BYTE)           NULL, 
	TOTAL_POINTS               		NUMBER                      NULL, 
	PREVIOUS_POINTS            		NUMBER                      NULL, 
	RANK_CHANGE                		NUMBER                      NULL, 
	CUR_YEAR_AVG               		NUMBER                      NULL, 
	CUR_YEAR_AVG_WEIGHTED     		NUMBER                      NULL, 
	LAST_YEAR_AVG              		NUMBER                      NULL, 
	LAST_YEAR_AVG_WEIGHTED    		NUMBER                      NULL, 
	TWO_YEAR_AGO_AVG           		NUMBER                      NULL, 
	TWO_YEAR_AGO_WEIGHTED    		NUMBER                      NULL, 
	THREE_YEAR_AGO_AVG         		NUMBER                      NULL, 
	THREE_YEAR_AGO_WEIGHTED         NUMBER                      NULL, 
	CONFEDERATION              		VARCHAR2(10 BYTE)           NULL
);	

ALTER TABLE SYSTEM.FIFA_RANKING ADD
(
    CONSTRAINT PK_FIFA_RANKING
    PRIMARY KEY ( RNK, RNK_DATE )
        USING INDEX
        TABLESPACE SYSTEM 
        PCTFREE 10
        INITRANS 2
        MAXTRANS 255
        STORAGE (
            INITIAL 64 K
            NEXT 1 M
            MINEXTENTS 1
            MAXEXTENTS UNLIMITED
            FREELISTS 1
            FREELIST GROUPS 1
        )
);


[소스레파지토리]

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

반응형

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

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
FILE TO DB - Fixed Length 예제  (0) 2018.10.22