multi-datasource 지원 추가

master
mjkhan21 4 months ago
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&#47;**&#47;*.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;
}
}

@ -9,7 +9,6 @@ import java.util.stream.Collectors;
import org.egovframe.rte.fdl.cmmn.trace.LeaveaTrace; import org.egovframe.rte.fdl.cmmn.trace.LeaveaTrace;
import org.egovframe.rte.fdl.property.impl.EgovPropertyServiceImpl; import org.egovframe.rte.fdl.property.impl.EgovPropertyServiceImpl;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
@ -21,7 +20,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author mjkhan * @author mjkhan
*/ */
@Configuration @Configuration
@ComponentScan(basePackages = "cokr.xit")
public class CommonConfig { public class CommonConfig {
/**AntPathMatcher . /**AntPathMatcher .
* @return AntPathMatcher * @return AntPathMatcher

@ -1,20 +1,13 @@
package cokr.xit.foundation.boot; package cokr.xit.foundation.boot;
import java.util.Properties;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider; import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.egovframe.rte.psl.dataaccess.mapper.MapperConfigurer; import org.egovframe.rte.psl.dataaccess.mapper.MapperConfigurer;
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import cokr.xit.foundation.Assert;
import cokr.xit.foundation.data.paging.MapperSupport;
/** /**
* <ul><li>{@link #dataSource() } </li> * <ul><li>{@link #dataSource() } </li>
@ -24,9 +17,7 @@ import cokr.xit.foundation.data.paging.MapperSupport;
* @author mjkhan * @author mjkhan
*/ */
@Configuration @Configuration
public class DatasourceConfig { public class DatasourceConfig extends AbstractDatasource {
private DataSource dataSource;
/** Bean . JDBC application.yml . /** Bean . JDBC application.yml .
* <pre><code> spring: * <pre><code> spring:
* datasource: * datasource:
@ -37,10 +28,11 @@ public class DatasourceConfig {
* password: </code></pre> * password: </code></pre>
* @return * @return
*/ */
@Override
@Bean("dataSource") @Bean("dataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari") @ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource() { public DataSource dataSource() {
return dataSource != null ? dataSource : (dataSource = DataSourceBuilder.create().build()); return super.dataSource();
} }
/**SqlSessionFactoryBean .<br /> /**SqlSessionFactoryBean .<br />
@ -50,42 +42,25 @@ public class DatasourceConfig {
* </ul> * </ul>
* @return SqlSessionFactoryBean * @return SqlSessionFactoryBean
*/ */
@Override
@Bean @Bean
public SqlSessionFactoryBean sqlSession() { public SqlSessionFactoryBean sqlSession() {
try { return super.sqlSession();
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
bean.setConfigLocation(resolver.getResource("classpath:sql/mybatis-config.xml"));
bean.setMapperLocations(resolver.getResources("classpath:sql/mapper/**/*.xml"));
bean.setPlugins(new MapperSupport());
bean.setDatabaseIdProvider(databaseIdProvider());
return bean;
} catch (Exception e) {
throw Assert.runtimeException(e);
}
} }
/**MapperConfigurer .<br /> /**MapperConfigurer .<br />
* base package cokr.xit . * base package cokr.xit .
* @return MapperConfigurer * @return MapperConfigurer
*/ */
@Override
@Bean @Bean
public MapperConfigurer mapperConfigurer() { public MapperConfigurer mapperConfigurer() {
MapperConfigurer bean = new MapperConfigurer(); return super.mapperConfigurer();
bean.setBasePackage("cokr.xit");
bean.setSqlSessionFactoryBeanName("sqlSession");
return bean;
} }
@Override
@Bean @Bean
VendorDatabaseIdProvider databaseIdProvider() { public VendorDatabaseIdProvider databaseIdProvider() {
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider(); return super.databaseIdProvider();
Properties properties = new Properties();
properties.put("MariaDB", "mariadb");
properties.put("Oracle", "oracle");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
} }
} }

@ -2,6 +2,7 @@ package cokr.xit.foundation.boot;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
/**Spring Boot xit-foundation.jar /**Spring Boot xit-foundation.jar
@ -18,7 +19,8 @@ import org.springframework.test.context.ContextConfiguration;
ServletConfig.class, ServletConfig.class,
MvcConfig.class, MvcConfig.class,
DatasourceConfig.class, DatasourceConfig.class,
TransactionConfig.class, TransactionConfig.class
}) })
@ComponentScan(basePackages = "cokr.xit")
@ContextConfiguration("classpath:spring/context-*.xml") @ContextConfiguration("classpath:spring/context-*.xml")
public class FoundationApplication {} public class FoundationApplication {}

@ -6,6 +6,7 @@ import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
/**Spring Boot xit-foundation.jar stand-alone /**Spring Boot xit-foundation.jar stand-alone
@ -22,8 +23,9 @@ import org.springframework.test.context.ContextConfiguration;
@ImportAutoConfiguration({ @ImportAutoConfiguration({
CommonConfig.class, CommonConfig.class,
DatasourceConfig.class, DatasourceConfig.class,
TransactionConfig.class, TransactionConfig.class
}) })
@ComponentScan(basePackages = "cokr.xit")
@ContextConfiguration("classpath:spring/context-*.xml") @ContextConfiguration("classpath:spring/context-*.xml")
public class StandAloneApplication implements CommandLineRunner { public class StandAloneApplication implements CommandLineRunner {
@Override @Override

@ -1,24 +1,15 @@
package cokr.xit.foundation.boot; package cokr.xit.foundation.boot;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor; import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor; import org.springframework.transaction.interceptor.TransactionInterceptor;
/** . /** .
@ -29,51 +20,28 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
@Aspect @Aspect
@EnableTransactionManagement @EnableTransactionManagement
@EnableAspectJAutoProxy(proxyTargetClass = true) @EnableAspectJAutoProxy(proxyTargetClass = true)
public class TransactionConfig { public class TransactionConfig extends AbstractTransaction {
@Override
@Resource(name = "dataSource") @Resource(name = "dataSource")
private DataSource dataSource; public void setDataSource(DataSource dataSource) {
super.setDataSource(dataSource);
}
@Override
@Bean @Bean
public DataSourceTransactionManager txManager() { public DataSourceTransactionManager txManager() {
DataSourceTransactionManager bean = new DataSourceTransactionManager(); return super.txManager();
bean.setDataSource(dataSource);
return bean;
} }
@Override
@Bean @Bean
public TransactionInterceptor txAdvice() { public TransactionInterceptor txAdvice() {
RuleBasedTransactionAttribute read = new RuleBasedTransactionAttribute( return super.txAdvice();
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;
} }
@Override
@Bean @Bean
public Advisor txAdvisor() { public Advisor txAdvisor() {
String expression = Foundation.transactionPointcut(); return super.txAdvisor();
AspectJExpressionPointcut requiredTx = new AspectJExpressionPointcut();
requiredTx.setExpression(expression);
DefaultPointcutAdvisor bean = new DefaultPointcutAdvisor();
bean.setPointcut(requiredTx);
bean.setAdvice(txAdvice());
return bean;
} }
} }
Loading…
Cancel
Save