multi-datasource 지원 추가
parent
036ffbb552
commit
39f6e58284
@ -0,0 +1,124 @@
|
|||||||
|
package cokr.xit.foundation.boot;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
|
||||||
|
import org.egovframe.rte.psl.dataaccess.mapper.MapperConfigurer;
|
||||||
|
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||||
|
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
|
||||||
|
import cokr.xit.foundation.AbstractComponent;
|
||||||
|
import cokr.xit.foundation.Assert;
|
||||||
|
import cokr.xit.foundation.data.paging.MapperSupport;
|
||||||
|
|
||||||
|
/**데이터베이스 접속 관련 설정
|
||||||
|
* <ul><li>{@link #dataSource() 데이터소스} 설정</li>
|
||||||
|
* <li>{@link #sqlSession() MyBatis} 접속 설정</li>
|
||||||
|
* <li>{@link #mapperConfigurer() 매퍼} 설정</li>
|
||||||
|
* </ul>
|
||||||
|
* @author mjkhan
|
||||||
|
*/
|
||||||
|
public abstract class AbstractDatasource extends AbstractComponent {
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
/**데이터소스 Bean을 반환한다. JDBC 접속은 application.yml 파일에 다음과 같이 설정한다.
|
||||||
|
* <pre><code> spring:
|
||||||
|
* datasource:
|
||||||
|
* hikari:
|
||||||
|
* driver-class-name: JDBC 드라이버 클래스 이름
|
||||||
|
* jdbc-url: 데이터베이스 접속 URL
|
||||||
|
* username: 데이터베이스 접속 아이디
|
||||||
|
* password: 데이터베이스 접속 비밀번호</code></pre>
|
||||||
|
* @return 데이터소스
|
||||||
|
*/
|
||||||
|
protected DataSource dataSource() {
|
||||||
|
return dataSource != null ? dataSource : (dataSource = DataSourceBuilder.create().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**SqlSessionFactoryBean을 반환한다.<br />
|
||||||
|
* MyBatis의 설정 파일들은 다음 경로에 있어야 한다.
|
||||||
|
* <ul><li>MyBatis 설정: classpath:sql/mybatis-config.xml</li>
|
||||||
|
* <li>매퍼 xml: classpath:sql/mapper/**/*.xml</li>
|
||||||
|
* </ul>
|
||||||
|
* @return SqlSessionFactoryBean
|
||||||
|
*/
|
||||||
|
protected SqlSessionFactoryBean sqlSession() {
|
||||||
|
try {
|
||||||
|
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
|
||||||
|
bean.setDataSource(dataSource());
|
||||||
|
|
||||||
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
|
bean.setConfigLocation(resolver.getResource("classpath:sql/mybatis-config.xml"));
|
||||||
|
|
||||||
|
Resource[] mapperLocations = getMapperLocations(resolver);
|
||||||
|
if (!isEmpty(mapperLocations))
|
||||||
|
bean.setMapperLocations(mapperLocations);
|
||||||
|
bean.setPlugins(new MapperSupport());
|
||||||
|
bean.setDatabaseIdProvider(databaseIdProvider());
|
||||||
|
return bean;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw Assert.runtimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**매퍼 위치목록을 반환한다.
|
||||||
|
* @param resolver PathMatchingResourcePatternResolver
|
||||||
|
* @return 매퍼 위치목록
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private Resource[] getMapperLocations(PathMatchingResourcePatternResolver resolver) throws Exception {
|
||||||
|
List<Resource> resources = Stream.of(mapperLocationPatterns())
|
||||||
|
.flatMap(location -> {
|
||||||
|
try {
|
||||||
|
return Stream.of(resolver.getResources(location));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw runtimeException(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
return resources.toArray(new Resource[resources.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**매퍼 경로패턴을 반환한다.
|
||||||
|
* @return 매퍼 경로패턴 목록
|
||||||
|
*/
|
||||||
|
protected String[] mapperLocationPatterns() {
|
||||||
|
return new String[] {"classpath:sql/mapper/**/*.xml"};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**MapperConfigurer를 반환한다.<br />
|
||||||
|
* base package는 cokr.xit로 설정한다.
|
||||||
|
* @return MapperConfigurer
|
||||||
|
*/
|
||||||
|
protected MapperConfigurer mapperConfigurer() {
|
||||||
|
MapperConfigurer bean = new MapperConfigurer();
|
||||||
|
String basePackages = mapperBasePackages();
|
||||||
|
if (!isEmpty(basePackages))
|
||||||
|
bean.setBasePackage(basePackages);
|
||||||
|
bean.setSqlSessionFactoryBeanName(sqlSessionName());
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String mapperBasePackages() {
|
||||||
|
return "cokr.xit";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String sqlSessionName() {
|
||||||
|
return "sqlSession";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected VendorDatabaseIdProvider databaseIdProvider() {
|
||||||
|
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.put("MariaDB", "mariadb");
|
||||||
|
properties.put("Oracle", "oracle");
|
||||||
|
databaseIdProvider.setProperties(properties);
|
||||||
|
return databaseIdProvider;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package cokr.xit.foundation.boot;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.springframework.aop.Advisor;
|
||||||
|
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||||
|
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||||
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionDefinition;
|
||||||
|
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
|
||||||
|
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
|
||||||
|
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
|
||||||
|
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||||
|
|
||||||
|
/**트랜잭션 설정 클래스.
|
||||||
|
* <p>트랜잭션의 적용 대상은 cokr.xit..service.bean..*ServiceBean 클래스의 메소드들이다.
|
||||||
|
* @author mjkhan
|
||||||
|
*/
|
||||||
|
public class AbstractTransaction {
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
public DataSource getDataSource() {
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDataSource(DataSource dataSource) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DataSourceTransactionManager txManager() {
|
||||||
|
DataSourceTransactionManager bean = new DataSourceTransactionManager();
|
||||||
|
bean.setDataSource(getDataSource());
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TransactionInterceptor txAdvice() {
|
||||||
|
RuleBasedTransactionAttribute read = new RuleBasedTransactionAttribute(
|
||||||
|
TransactionDefinition.PROPAGATION_REQUIRED,
|
||||||
|
List.of(new RollbackRuleAttribute(Throwable.class))
|
||||||
|
);
|
||||||
|
read.setReadOnly(true);
|
||||||
|
RuleBasedTransactionAttribute write = new RuleBasedTransactionAttribute(
|
||||||
|
TransactionDefinition.PROPAGATION_REQUIRED,
|
||||||
|
List.of(new RollbackRuleAttribute(Throwable.class))
|
||||||
|
);
|
||||||
|
|
||||||
|
NameMatchTransactionAttributeSource txAttrSrc = new NameMatchTransactionAttributeSource();
|
||||||
|
txAttrSrc.setNameMap(Map.of(
|
||||||
|
"get*", read,
|
||||||
|
"*", write
|
||||||
|
));
|
||||||
|
|
||||||
|
TransactionInterceptor bean = new TransactionInterceptor();
|
||||||
|
bean.setTransactionManager(txManager());
|
||||||
|
bean.setTransactionAttributeSource(txAttrSrc);
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Advisor txAdvisor() {
|
||||||
|
String expression = Foundation.transactionPointcut();
|
||||||
|
AspectJExpressionPointcut requiredTx = new AspectJExpressionPointcut();
|
||||||
|
requiredTx.setExpression(expression);
|
||||||
|
|
||||||
|
DefaultPointcutAdvisor bean = new DefaultPointcutAdvisor();
|
||||||
|
bean.setPointcut(requiredTx);
|
||||||
|
bean.setAdvice(txAdvice());
|
||||||
|
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue