Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- maven #junit
- 자바개발
- Tomcat error-page version
- SI와 SM 차이점
- SW설계
- EXK
- 자바스크립트 JQuery
- CBD 단점
- 객체설계
- 실해하지 않는
- Data Transfer Object(DTO)
- docker 네트워크
- SW분석
- ASUS 공유기
- sonoff
- 네트워크 기본개념
- tvheadend
- 프로젝트관리
- 구글캘린더 검색
- open cursors
- Value Object(VO)
- angular 6
- LG그램2017
- USB-C to HDMI 아답터
- USB-C충전
- SpringBoot
- LAN WAN
- 소프트웨어설계
- 경력개발자
- 자바튜닝
Archives
- Today
- Total
대빵's Blog
Atomikos를 이용한 이기종 DB 트랜잭션(Springboot + Mybatis) - 2. Application 환경구성 및 샘플코드 본문
개발관련
Atomikos를 이용한 이기종 DB 트랜잭션(Springboot + Mybatis) - 2. Application 환경구성 및 샘플코드
bigzero 2019. 8. 26. 15:15Atomikos를 이용한 이기종 DB 트랜잭션(Springboot + Mybatis) - 2. Application 환경구성 및 샘플코드
Springboot 및 Mybatis 를 위한 Config 설정
-
application.properties 정의
spring.jta.enabled=true
# DATASOURCE master => develop server [XXX_DB] (Single DB Transaction)
#spring.db.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
#spring.db.datasource.jdbc-url=jdbc:sqlserver://메인DB서버IP:포트;databaseName=XXX_DB
spring.db.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.db.datasource.jdbc-url=jdbc:log4jdbc:sqlserver://메인DB서버IP:포트;databaseName=XXX_DB
spring.db.datasource.username=메인DB서버계정
spring.db.datasource.password=메인DB서버암호
# Multiple Transaction Define
spring.db1.datasource.xa-data-source-class-name=com.microsoft.sqlserver.jdbc.SQLServerXADataSource
spring.db1.datasource.xa-properties.server-name=멀티DB서버1번IP # Local Transaction 을 사용하는 DBMS와 동일한 DB 이다.
spring.db1.datasource.xa-properties.port-number=포트
spring.db1.datasource.xa-properties.database-name=XXX_DB #DBMS 마다 DB 스키마 지정방법이 다르다...주의!
spring.db1.datasource.xa-properties.user=멀티DB서버1번계정
spring.db1.datasource.xa-properties.password=멀티DB서버1번암호
# TEST => localhost
spring.db2.datasource.xa-data-source-class-name=org.postgresql.xa.PGXADataSource
spring.db2.datasource.xa-properties.server-name=멀티DB서버2번IP
spring.db2.datasource.xa-properties.port-number=포트
spring.db2.datasource.xa-properties.current-schema=xxx_interface #DBMS 마다 DB 스키마 지정방법이 다르다...주의!
spring.db2.datasource.xa-properties.user=멀티DB서버2번계정
spring.db2.datasource.xa-properties.password=멀티DB서버2번암호
-
일단 멀티가 아닌 Local Transaction 을 사용하기 위한 설정 - Primary를 설정해야 한다.
-
Multi Transaction 을 사용하지 않는 Single Transaction 을 DB Config 로 먼저 구성한다. 일반적인 @Transaction 을 사용하면 아래 Config 내용이 적용된다.(@Primary 때문에)
-
mapper1 는 이기종 DBMS 설정과 같이 사용할 것이기 때문에 같은 패키기 구조로 정의했다. 만일 Local Transaction 을 사용하는 DBMS 와 이기종 1,2 번이 모두 다른 DBMS 이면 mapper를 서로 다르게 설정해야 한다.
-
만일 모든 DBMS를 동일한 쿼리를 사용하고 싶다면 굳이 mapper(쿼리)를 나누지 않아도 된다. 단, 헷갈리기 시작하면 답이 없으므로 DBMS 에 따라 Mapper를 나누는 것을 권장한다.
-
@Configuration
-
@EnableTransactionManagement
-
@MapperScan(basePackages="com.xxx.integration.**.mapper1", sqlSessionFactoryRef="dsSqlSessionFactory")
-
public class XxxDbConfig {
-
-
private final Logger logger = LoggerFactory.getLogger(XxxDbConfig.class);
-
-
public static final String DS_DATASOURCE = "dsDataSource";
-
-
/* ================> data source 1 start */
-
@ConfigurationProperties(prefix="spring.db.datasource")
-
@Bean(name = DS_DATASOURCE)
-
public DataSource dataSource() {
-
return DataSourceBuilder.create().build();
-
}
-
-
// Transaction 을 명시적으로 선언하여 이름을 불러서 사용하려면 enable 시켜야 됨.
-
// Spring Global Tx 를 사용하려면 JTA 와 JNDI 를 사용해야 한다.
-
// Local Tx 는 1개의 txManager 만 허용하므로 Prime 이 필요하다.
-
@Primary
-
@Bean(name = "dsTxManager")
-
public PlatformTransactionManager transactionManager (@Qualifier(DS_DATASOURCE) DataSource dataSource){
-
return new DataSourceTransactionManager(dataSource);
-
}
-
-
@Bean(name="dsSqlSessionFactory")
-
public SqlSessionFactory sqlSessionFactory(@Qualifier(DS_DATASOURCE) DataSource dataSource) throws Exception {
-
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
-
sessionFactory.setDataSource(dataSource);
-
return sessionFactory.getObject();
-
}
-
}
-
이기종 트랜잭션 처리를 위한 멀티 DBMS 1번 설정
-
서로 다른 쿼리 실행을 위한 첫번째 DB 의 mybatis 설정(보통 Single DB 의 구성에 드라이버만 XA 로 변경되도록 한다.)
-
/**
-
* <pre>
-
* com.xxx.integration.config
-
* XxxDb1Config.java
-
* </pre>
-
*
-
* @Author : Heo Dae-Young
-
* @Date : 2019. 4. 22.
-
* @Version : 1.0
-
* 일단 서로 다른 두 종류의 DB 이기 때문에 쿼리 문장이 다를 수 있으므로 mapper 분리를 위해
-
* mybatis mapper 를 폴더로 분리하고 sqlSessionFactory 를 분리한다.
-
* sqlserver 가 jdbc-url 프로퍼티를 정상적으로 인식하지 않기 때문에 value annotation 을 사용하여 프로퍼티값을 읽어온다.
-
*
-
*/
-
@Configuration
-
@EnableTransactionManagement
-
@MapperScan(basePackages="com.xxx.integration.**.mapper1", sqlSessionFactoryRef="ds1SqlSessionFactory")
-
public class XxxDb1Config {
-
-
@Value("${spring.db1.datasource.xa-data-source-class-name}") String ds1XaDataSourceClassName;
-
@Value("${spring.db1.datasource.xa-properties.user}") String ds1User;
-
@Value("${spring.db1.datasource.xa-properties.password}") String ds1Password;
-
@Value("${spring.db1.datasource.xa-properties.server-name}") String ds1ServerName;
-
@Value("${spring.db1.datasource.xa-properties.port-number}") String ds1PortNumber;
-
@Value("${spring.db1.datasource.xa-properties.database-name}") String ds1DatabaseName;
-
-
private final Logger logger = LoggerFactory.getLogger(XxxDb1Config.class);
-
-
public static final String DS1_DATASOURCE = "ds1DataSource";
-
-
/* ================> data source 1 start */
-
@Bean(name = DS1_DATASOURCE)
-
public DataSource dataSource() {
-
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
-
ds.setUniqueResourceName(DS1_DATASOURCE);
-
ds.setXaDataSourceClassName(ds1XaDataSourceClassName);
-
-
Properties p = new Properties();
-
p.setProperty("user", ds1User);
-
p.setProperty("password", ds1Password);
-
p.setProperty("serverName", ds1ServerName);
-
p.setProperty("portNumber", ds1PortNumber);
-
p.setProperty("databaseName", ds1DatabaseName);
-
ds.setXaProperties (p);
-
-
return ds;
-
}
-
-
@Bean(name="ds1SqlSessionFactory")
-
public SqlSessionFactory sqlSessionFactory(@Qualifier(DS1_DATASOURCE) DataSource dataSource) throws Exception {
-
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
-
sessionFactory.setDataSource(dataSource);
-
return sessionFactory.getObject();
-
}
-
}
-
두번째 DB 의 mybatis 설정 정보
-
/**
-
* <pre>
-
* com.xxx.integration.config
-
* XxxlDb2Config.java
-
* </pre>
-
*
-
* @Author : Heo Dae-Young
-
* @Date : 2019. 4. 22.
-
* @Version : 1.0
-
*
-
* 일단 서로 다른 두 종류의 DB 이기 때문에 쿼리 문장이 다를 수 있으므로 mapper 분리를 위해
-
* mybatis mapper 를 폴더로 분리하고 sqlSessionFactory 를 분리한다.
-
*
-
*/
-
@Configuration
-
@EnableTransactionManagement
-
@MapperScan(basePackages="com.xxx.integration.**.mapper2",sqlSessionFactoryRef="ds2SqlSessionFactory")
-
public class XxxDb2Config {
-
-
@Value("${spring.db2.datasource.xa-data-source-class-name}") String ds2XaDataSourceClassName;
-
@Value("${spring.db2.datasource.xa-properties.user}") String ds2User;
-
@Value("${spring.db2.datasource.xa-properties.password}") String ds2Password;
-
@Value("${spring.db2.datasource.xa-properties.server-name}") String ds2ServerName;
-
@Value("${spring.db2.datasource.xa-properties.port-number}") String ds2PortNumber;
-
@Value("${spring.db2.datasource.xa-properties.current-schema}") String ds2CurrentSchema;
-
-
private final Logger logger = LoggerFactory.getLogger(XxxDb2Config.class);
-
-
public static final String DS2_DATASOURCE = "ds2DataSource";
-
-
/* ================> data source 2 start */
-
@Bean(name=DS2_DATASOURCE)
-
public DataSource dataSource() {
-
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
-
ds.setUniqueResourceName(DS2_DATASOURCE);
-
ds.setXaDataSourceClassName(ds2XaDataSourceClassName);
-
-
Properties p = new Properties();
-
p.setProperty("user", ds2User);
-
p.setProperty("password", ds2Password);
-
p.setProperty("serverName", ds2ServerName);
-
p.setProperty("portNumber", ds2PortNumber);
-
p.setProperty("currentSchema", ds2CurrentSchema);
-
ds.setXaProperties (p);
-
-
return ds;
-
}
-
-
@Bean(name="ds2SqlSessionFactory")
-
public SqlSessionFactory sqlSessionFactory(@Qualifier(DS2_DATASOURCE) DataSource dataSource) throws Exception {
-
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
-
sessionFactory.setDataSource(dataSource);
-
return sessionFactory.getObject();
-
}
-
}
-
mybatis 가 spring 에게 위임한 PlatformTransactionManager 를 Atomikos 로 변경한다.
-
아래 맨 마지막에 PlatformTransactionManger 로서 별도의 Bean을 정의하여 JtaTransactionManger 로 리턴하게끔 설정하는 것이 핵심이다.
-
/**
-
* <pre>
-
* com.xxx.integration.config
-
* XxxMultiTxConfig.java
-
* </pre>
-
*
-
* @Author : Heo Dae-Young
-
* @Date : 2019. 4. 22.
-
* @Version : 1.0
-
* Multi Transaction 처리를 위한 정의
-
* UserTransaction 과 AtomikosTransaction 을 두개로 정의하고 두개의 Transaction 을 PlatformTransactionManager 하나로 묶는다.
-
*/
-
@Configuration
-
@EnableTransactionManagement
-
public class XxxMultiTxConfig {
-
-
private final Logger logger = LoggerFactory.getLogger(XxxMultiTxConfig.class);
-
-
@Bean(name = "userTransaction")
-
public UserTransaction userTransaction() throws Throwable {
-
UserTransactionImp userTransactionImp = new UserTransactionImp();
-
userTransactionImp.setTransactionTimeout(10000);
-
return userTransactionImp;
-
}
-
-
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
-
public TransactionManager atomikosTransactionManager() throws Throwable {
-
UserTransactionManager userTransactionManager = new UserTransactionManager();
-
userTransactionManager.setForceShutdown(false);
-
return userTransactionManager;
-
}
-
-
@Bean(name = "multiTxManager")
-
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
-
public PlatformTransactionManager transactionManager() throws Throwable {
-
UserTransaction userTransaction = userTransaction();
-
JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());
-
return manager;
-
}
-
-
}
-
-
테스트 코드 - 이기종 Mapper 2개를 Service 에서 단일 트랜잭션으로 묶어준다.
-
해당 Service는 JUNIT4.x 에서 테스트 했다.
-
두개의 서로다른 Mapper 를 @Transactional 에서 multiTxManager 라는 Transaction 으로 묶어준다.
-
@Service
-
public class CommonService {
-
-
@Autowired CommonMapper1 sqlServerDao;
-
@Autowired CommonMapper2 postgreDao;
-
-
@Transactional(value="multiTxManager")
-
public int insertTxTest() {
-
-
int result = 0;
-
-
List<Map<String, String>> dataMapList = new ArrayList<>();
-
Map<String, String> dataMap = null;
-
for(int i=0,j=10 ; i<j ; i++) {
-
dataMap = new HashMap<>();
-
dataMap.put("C1", i+":key");
-
dataMap.put("C2", i+":value");
-
dataMapList.add(dataMap);
-
}
-
-
for(int i=0 ,j=10 ; i<j ; i++) {
-
result = result + sqlServerDao.insertTxTestSqlServer(dataMapList.get(i));
-
result = result + postgreDao.insertTxTestPostgre(dataMapList.get(i));
-
}
-
return result;
-
}
-
-
}
-
참고 이기종 DBMS 2번이 Postgre 가 아닌 오라클 인 경우
-
오라클인 경우 property 에 ULR로 설정해야 한다.
-
@Configuration
-
@EnableTransactionManagement
-
@MapperScan(basePackages="com.xxx.integration.**.mapper2",sqlSessionFactoryRef="ds2SqlSessionFactory")
-
public class XxxDb2Config {
-
-
@Value("${spring.db2.datasource.xa-data-source-class-name}") String ds2XaDataSourceClassName;
-
@Value("${spring.db2.datasource.xa-properties.user}") String ds2User;
-
@Value("${spring.db2.datasource.xa-properties.password}") String ds2Password;
-
@Value("${spring.db2.datasource.xa-properties.url}") String ds2Url; // 이것 때문에 무지 삽질했다...
-
-
private final Logger logger = LoggerFactory.getLogger(XxxDb2Config.class);
-
-
public static final String DS2_DATASOURCE = "ds2DataSource";
-
-
/* ================> data source 2 start */
-
@Bean(name=DS2_DATASOURCE)
-
public DataSource dataSource() {
-
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
-
ds.setUniqueResourceName(DS2_DATASOURCE);
-
ds.setXaDataSourceClassName(ds2XaDataSourceClassName);
-
-
Properties p = new Properties();
-
p.setProperty("user", ds2User);
-
p.setProperty("password", ds2Password);
-
p.setProperty("URL", ds2Url); // 이것이 문제였다...젠장...
-
-
ds.setXaProperties (p);
-
-
return ds;
-
}
-
-
@Bean(name="ds2SqlSessionFactory")
-
public SqlSessionFactory sqlSessionFactory(@Qualifier(DS2_DATASOURCE) DataSource dataSource) throws Exception {
-
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
-
sessionFactory.setDataSource(dataSource);
-
return sessionFactory.getObject();
-
}
'개발관련' 카테고리의 다른 글
윈도우 이클립스 Maven 속도향상 (0) | 2020.03.16 |
---|---|
Scouter APM 에서 HikariCP 모니터링 하기 (0) | 2020.01.09 |
Atomikos를 이용한 이기종 DB 트랜잭션(SpringBoot + Mybatis) - 1.환경구성 (0) | 2019.08.26 |
Maven 과 junit 의 관계(Maven properties in Junit) (0) | 2019.04.20 |
원노트에 코드블록 이쁘게 삽입하기 (0) | 2019.01.17 |