关于 Spring For All

关于 Spring For All

Spring For All 的一切
最新动态

最新动态

Spring 5 会是咋样呢
Spring Boot

Spring Boot

快速构建并运行 Spring 应用程序
Spring Cloud

Spring Cloud

分布式系统的一套工具,可用于构建微服务
Spring Framework

Spring Framework

提供依赖注入、事务、Web应用、数据访问等模块
Spring Data

Spring Data

提供一致性数据访问模块
Spring Security

Spring Security

提供应用身份验证和授权支持
Spring Batch

Spring Batch

提供高容批处理操作模块
Spring AMQP

Spring AMQP

基于AMQP消息解决方案
Micro Service Arch.

Micro Service Arch.

微服务架构相关
开源项目及视频教程

开源项目及视频教程

做中国最好的 Spring 开源项目及视频教程
小马哥专栏

小马哥专栏

阿里技术布道者,专注 Spring Boot 及微服务

Spring boot启动报错 dataSourceInitializerPostProcessor

回复

Spring BootZhengDuan 发起了问题 • 1 人关注 • 0 个回复 • 105 次浏览 • 2017-09-13 10:08 • 来自相关话题

springCloud 配置中心 refresh 的时候异常

回复

Spring Cloudwolikezk 发起了问题 • 1 人关注 • 0 个回复 • 110 次浏览 • 2017-09-13 10:01 • 来自相关话题

个人小站CodeSheep新增后台管理功能,欢迎使用(网址 http://113.209.119.170/)

开源项目hansonwang99 发表了文章 • 2 个评论 • 296 次浏览 • 2017-09-12 22:10 • 来自相关话题

个人小站CodeSheep新增后台管理功能,该功能使用vue2.0开发,使用了全新的开发方式,目前来讲我的个人小站的前端是混合开发的,一部分是基于thymeleaf的模版,一部分是基于vue.js的组件式的开发方式。除此之外,本网站上的小工具“身高百分位查询表”新增输入交互功能,用户可以输入信息来定位孩子的身高在曲线上的分布,帮助家长对孩子身高长势有个更好的知悉与干预作用






 
一、后台管理功能
















 
二、身高百分位表功能


















  查看全部
个人小站CodeSheep新增后台管理功能,该功能使用vue2.0开发,使用了全新的开发方式,目前来讲我的个人小站的前端是混合开发的,一部分是基于thymeleaf的模版,一部分是基于vue.js的组件式的开发方式。除此之外,本网站上的小工具“身高百分位查询表”新增输入交互功能,用户可以输入信息来定位孩子的身高在曲线上的分布,帮助家长对孩子身高长势有个更好的知悉与干预作用

IMG_8739.JPG


 
一、后台管理功能

后台管理.jpg


后台管理2.jpg


后台管理3.jpg


 
二、身高百分位表功能


身高百分位表1.jpg


身高百分位表2.jpg


身高百分位表3.jpg



 

注册到Eureka服务不自动注销

Spring Cloudwayne 回复了问题 • 2 人关注 • 2 个回复 • 238 次浏览 • 2017-09-12 16:29 • 来自相关话题

普通方式实现单例, 和枚举类实现单例, 那种方式更好呢?

Spring Bootbenjamin923 回复了问题 • 2 人关注 • 1 个回复 • 153 次浏览 • 2017-09-12 16:20 • 来自相关话题

springboot 1.5.6版本集成druid mybatis后事务不回滚

Spring Bootmuncie 回复了问题 • 4 人关注 • 2 个回复 • 339 次浏览 • 2017-09-12 13:58 • 来自相关话题

每个微服务之间都是通过共享数据库,共享缓存进行交互的,并没采用彼此之间的接口调用。

Micro Service Arch.采蘑菇的大叔 回复了问题 • 8 人关注 • 6 个回复 • 919 次浏览 • 2017-09-12 13:49 • 来自相关话题

数据分片解决方案??

Spring FrameworkLevin 回复了问题 • 2 人关注 • 1 个回复 • 116 次浏览 • 2017-09-12 13:29 • 来自相关话题

Spring学习(一): 使用AbstractRoutingDataSource类进行多数据源配置

Spring Datazane 发表了文章 • 0 个评论 • 169 次浏览 • 2017-09-12 12:33 • 来自相关话题

多数据源在项目中很常见,举几个需求,如页面展示的数据需要从不同的数据库中去查询,修改等,例如数据库的读写分离等

配置多数据源要考虑到如下的情况
单个数据源是如下这种情况:

如果是多个数据源的话,将会变成下面这种情况



这样,每增加一个数据源就要去配置一个sessionFactory,这样比较麻烦,于是spring的
AbstractRoutingDataSource类产生了


它的实现原理是扩展Spring的`AbstractRoutingDataSource`抽象类(该类充当了`DataSource`的路由中介, 能在运行时, 根据某种key值来动态切换到真正的`DataSource`上。)
 从AbstractRoutingDataSource的源码中:$(document).ready(function() {$('pre code').each(function(i, block) { hljs.highlightBlock( block); }); });public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean

我们可以看到,它继承了AbstractDataSource,而AbstractDataSource不就是javax.sql.DataSource的子类,So我们可以分析下它的getConnection方法:public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}

public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}

获取连接的方法中,重点是determineTargetDataSource()方法,看源码:protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}上面这段源码的重点在于determineCurrentLookupKey()方法,这是AbstractRoutingDataSource类中的一个抽象方法,而它的返回值是你所要用的数据源dataSource的key值,有了这个key值,resolvedDataSource(这是个map,由配置文件中设置好后存入的)就从中取出对应的DataSource,如果找不到,就用配置默认的数据源。
看完源码,应该有点启发了吧,没错!你要扩展AbstractRoutingDataSource类,并重写其中的determineCurrentLookupKey()方法,来实现数据源的切换:

接下来,将展示一个多数据源配置demo 

1.配置一个config类,配置3个数据源(下面是JAVA配置写法)/**
* Created by Youjie on 2017/7/23.
*/
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

@Bean
@ConfigurationProperties(prefix = "datasource.db_a")
@Qualifier("dataSourceDefault")
public DataSource dataSourceDefault(){
return new DruidDataSource();
}

@Bean
@ConfigurationProperties(prefix = "datasource.db_b")
@Qualifier("dataSourceB")
public DataSource dataSourceB(){
return new DruidDataSource();
}

@Bean
@ConfigurationProperties(prefix = "datasource.db_c")
@Qualifier("dataSourceC")
public DataSource dataSourceC(){
return new DruidDataSource();
}


@Bean
@Primary
public DataSource multipleDataSource(){
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(dataSourceDefault());

Map<Object,Object> dataSourcesType = new HashMap();
dataSourcesType.put(DataSources.DATASOURCE_A,dataSourceDefault());
dataSourcesType.put(DataSources.DATASOURCE_B,dataSourceB());
dataSourcesType.put(DataSources.DATASOURCE_C,dataSourceC());

dynamicDataSource.setTargetDataSources(dataSourcesType);

return dynamicDataSource;
}



@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(multipleDataSource());
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("com.may.model");
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("configuration.xml"));
return sqlSessionFactoryBean.getObject();
}

@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(multipleDataSource());
}



}对应的 application.properties如下server.port=9002
server.context-path=/


#A user
datasource.db_a.driver-class-name=com.mysql.jdbc.Driver
datasource.db_a.url=jdbc:mysql://127.0.0.1:3306/test_A?autoReconnect=true&useUnicode=true&characterEncoding=utf8&logSlowQueries=false&slowQueryThresholdMillis=0&allowMultiQueries=true
datasource.db_a.username=root
datasource.db_a.password=123456

#B city
datasource.db_b.driver-class-name=com.mysql.jdbc.Driver
datasource.db_b.url=jdbc:mysql://127.0.0.1:3306/test_B?autoReconnect=true&useUnicode=true&characterEncoding=utf8&logSlowQueries=false&slowQueryThresholdMillis=0&allowMultiQueries=true
datasource.db_b.username=root
datasource.db_b.password=123456

#C role
datasource.db_c.driver-class-name=com.mysql.jdbc.Driver
datasource.db_c.url=jdbc:mysql://127.0.0.1:3306/test_C?autoReconnect=true&useUnicode=true&characterEncoding=utf8&logSlowQueries=false&slowQueryThresholdMillis=0&allowMultiQueries=true
datasource.db_c.username=root
datasource.db_c.password=123456

2、写一个ThreadLocalDynamicDataSource类继承AbstractRoutingDataSource,并实现determineCurrentLookupKey方法public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceTypeManager.get();
}
}3、编写DataSourceTypeManager执行类,并利用ThreadLocal以空间换时间的方式解决线程安全问题public class DataSourceTypeManager {
private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
@Override
protected DataSources initialValue() {
return DataSources.DATASOURCE_DEFAULT;
}
};

public static DataSources get() {
return dataSourceTypes.get();
}

public static void set(DataSources dataSourceType) {
dataSourceTypes.set(dataSourceType);
}

public static void reset() {
dataSourceTypes.set(DataSources.DATASOURCE_DEFAULT);
}

public static void clearDataSources () {
dataSourceTypes.remove();
}
}




上面代码利用initialValue方法,调用初始化时指定的DataSources 类型,这里DataSources 为枚举类型,里面定义三种数据源的名称,代码如下:public enum DataSources {
DATASOURCE,DATASOURCE_DEFAULT,DATASOURCE_ORACLE
}
4、multipleDataSource多数据源配置的xml配置<bean id="multipleDataSource"
class="com.hxqc.basic.dependency.datasource.ThreadLocalRountingDataSource">
<property name="defaultTargetDataSource" ref="ncDataSource"/>
<property name="targetDataSources">
<map key-type="com.hxqc.basic.dependency.datasource.DataSources">
<entry key="DATASOURCE" value-ref="erpDataSource"/>
<entry key="DATASOURCE_DEFAULT" value-ref="ncDataSource"/>
<entry key="DATASOURCE_ORACLE" value-ref="oracleDataSource"/>
</map>
</property>
</bean>



multipleDataSource多数据源配置的Java配置(上文已经贴出该配置,这里再强调一下)@Bean
@Primary
public DataSource multipleDataSource(){
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(dataSourceDefault());

Map<Object,Object> dataSourcesType = new HashMap();
dataSourcesType.put(DataSources.DATASOURCE_A,dataSourceDefault());
dataSourcesType.put(DataSources.DATASOURCE_B,dataSourceB());
dataSourcesType.put(DataSources.DATASOURCE_C,dataSourceC());

dynamicDataSource.setTargetDataSources(dataSourcesType);

return dynamicDataSource;
}
5.在DAOImpl中切换数据源 或者配置aop切面根据不同的包下面的数据,采用不同的数据源



1,在Dao层直接切换数据源,如切换到默认数据源DataSourceTypeManager.set(DataSources.DATASOURCE_DEFAULT)
2.根据aop进行动态切换
aop xml配置<bean id="dataSourceInterceptor" class="com.hxqc.basic.dependency.interceptor.DataSourceInterceptor"/>
<aop:config>
<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
<aop:pointcut id="datasource" expression="execution(* com.hxqc.data.gather.core.erp.*.mapper..*.*(..))"/>
<aop:pointcut id="datasource_oracle" expression="execution(* com.hxqc.data.gather.core.oracle.*.mapper..*.*(..))"/>
<aop:pointcut id="datasource_default" expression="execution(* com.hxqc.data.gather.core.nc.*.mapper..*.*(..))"/>
<aop:before method="setDataSource" pointcut-ref="datasource"/>
<aop:before method="setDataSourceDefault" pointcut-ref="datasource_default"/>
<aop:before method="setDataSourceOracle" pointcut-ref="datasource_oracle"/>
</aop:aspect>
</aop:config>aop java 配置@Aspect
@Component
public class DataSourceAspect {

private Logger log = LoggerFactory.getLogger(getClass());

@Pointcut("execution(* com.may.dataA.mapper.*.*(..))")
public void pointCutA(){}

@Pointcut("execution(* com.may.dataB.mapper.*.*(..))")
public void pointCutB(){}

@Pointcut("execution(* com.may.dataC.mapper.*.*(..))")
public void pointCutC(){}
@Pointcut("execution(* com.may..*.*(..))")
public void annotation(){}



public void setDataSourceDefault(JoinPoint jp){
DataSourceManager.set(DataSources.DATASOURCE_A);
log.info("切换数据源","切换到默认数据源A");

}
public void setDataSourceB(JoinPoint jp){
DataSourceManager.set(DataSources.DATASOURCE_B);
log.info("切换数据源","切换到数据源B");
}

public void setDataSourceC(JoinPoint jp){
DataSourceManager.set(DataSources.DATASOURCE_C);
log.info("切换数据源","切换到数据源C");
}

@Before("@annotation(ds)")
public void setAnnotationDataSource(JoinPoint jp,DataSource ds){
// 获取当前的指定的数据源;
String dsId = ds.value();
// 如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。
/*if (!DataSourceManager.containsDataSource(dsId)) {

log.error("数据源[{}]不存在,使用默认数据源 > {}", ds.value(), jp.getSignature());

} else {

log.debug("Use DataSource : {} > {}", ds.value(), jp.getSignature());

// 找到的话,那么设置到动态数据源上下文中。
DynamicDataSourceContextHolder.setDataSourceType(ds.name());
}*/

//将string值转换为enum
DataSourceManager.set(Enum.valueOf(DataSources.class,ds.value()));

log.info("切换数据源"+ds.value(),"切换到默认数据源"+ds.value());
}



}
解释,这里定义三个切面,分别对应三个数据源,如在oracle包下的,动态切换到oracle数据源,上面配置的最后部分是用到了自定义注解DataSource注解,该注解在方法上,通过切面来进行数据源的切换,具体看源码

然后配置切面类/**
*
*
* @author maple
*
*/
public class DataSourceInterceptor {

public void setDataSourceDefault(JoinPoint jp){
DataSourceTypeManager.set(DataSources.DATASOURCE_DEFAULT);
LogU.i("切换数据源","切换到NC数据源");
System.out.println(jp.getTarget().getClass().getSimpleName());
}

public void setDataSource(JoinPoint jp){
DataSourceTypeManager.set(DataSources.DATASOURCE);
LogU.i("切换数据源","切换到ERP数据源");
}

public void setDataSourceOracle(JoinPoint jp){
DataSourceTypeManager.set(DataSources.DATASOURCE_ORACLE);
LogU.i("切换数据源","切换到Oracle数据源");
}
}





`问题:`多数据源切换是成功了,但牵涉到事务呢?单数据源事务是ok的,但如果多数据源需要同时使用一个事务呢?这个问题有点头大,网络上有人提出用atomikos开源项目实现JTA分布式事务处理。你怎么看?

注意:

本文源码放在码云上,注意,源码将XML配置的内容都改成了JAVA配置,项目是基于Spring-boot,也提供了自定义注解来改变动态数据源!

本文源码:http://git.oschina.net/youjie1/may-dynamic-datasource
原文

  查看全部


多数据源在项目中很常见,举几个需求,如页面展示的数据需要从不同的数据库中去查询,修改等,例如数据库的读写分离等


配置多数据源要考虑到如下的情况
单个数据源是如下这种情况:

如果是多个数据源的话,将会变成下面这种情况



这样,每增加一个数据源就要去配置一个sessionFactory,这样比较麻烦,于是spring的
AbstractRoutingDataSource类产生了


它的实现原理是扩展Spring的`AbstractRoutingDataSource`抽象类(该类充当了`DataSource`的路由中介, 能在运行时, 根据某种key值来动态切换到真正的`DataSource`上。)
 从AbstractRoutingDataSource的源码中:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean


我们可以看到,它继承了AbstractDataSource,而AbstractDataSource不就是javax.sql.DataSource的子类,So我们可以分析下它的getConnection方法:
public Connection getConnection() throws SQLException {  
return determineTargetDataSource().getConnection();
}

public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}


获取连接的方法中,重点是determineTargetDataSource()方法,看源码:
protected DataSource determineTargetDataSource() {  
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
上面这段源码的重点在于determineCurrentLookupKey()方法,这是AbstractRoutingDataSource类中的一个抽象方法,而它的返回值是你所要用的数据源dataSource的key值,有了这个key值,resolvedDataSource(这是个map,由配置文件中设置好后存入的)就从中取出对应的DataSource,如果找不到,就用配置默认的数据源。
看完源码,应该有点启发了吧,没错!你要扩展AbstractRoutingDataSource类,并重写其中的determineCurrentLookupKey()方法,来实现数据源的切换:

接下来,将展示一个多数据源配置demo 

1.配置一个config类,配置3个数据源(下面是JAVA配置写法)
/**
* Created by Youjie on 2017/7/23.
*/
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

@Bean
@ConfigurationProperties(prefix = "datasource.db_a")
@Qualifier("dataSourceDefault")
public DataSource dataSourceDefault(){
return new DruidDataSource();
}

@Bean
@ConfigurationProperties(prefix = "datasource.db_b")
@Qualifier("dataSourceB")
public DataSource dataSourceB(){
return new DruidDataSource();
}

@Bean
@ConfigurationProperties(prefix = "datasource.db_c")
@Qualifier("dataSourceC")
public DataSource dataSourceC(){
return new DruidDataSource();
}


@Bean
@Primary
public DataSource multipleDataSource(){
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(dataSourceDefault());

Map<Object,Object> dataSourcesType = new HashMap();
dataSourcesType.put(DataSources.DATASOURCE_A,dataSourceDefault());
dataSourcesType.put(DataSources.DATASOURCE_B,dataSourceB());
dataSourcesType.put(DataSources.DATASOURCE_C,dataSourceC());

dynamicDataSource.setTargetDataSources(dataSourcesType);

return dynamicDataSource;
}



@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(multipleDataSource());
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("com.may.model");
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("configuration.xml"));
return sqlSessionFactoryBean.getObject();
}

@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(multipleDataSource());
}



}
对应的 application.properties如下
server.port=9002
server.context-path=/


#A user
datasource.db_a.driver-class-name=com.mysql.jdbc.Driver
datasource.db_a.url=jdbc:mysql://127.0.0.1:3306/test_A?autoReconnect=true&useUnicode=true&characterEncoding=utf8&logSlowQueries=false&slowQueryThresholdMillis=0&allowMultiQueries=true
datasource.db_a.username=root
datasource.db_a.password=123456

#B city
datasource.db_b.driver-class-name=com.mysql.jdbc.Driver
datasource.db_b.url=jdbc:mysql://127.0.0.1:3306/test_B?autoReconnect=true&useUnicode=true&characterEncoding=utf8&logSlowQueries=false&slowQueryThresholdMillis=0&allowMultiQueries=true
datasource.db_b.username=root
datasource.db_b.password=123456

#C role
datasource.db_c.driver-class-name=com.mysql.jdbc.Driver
datasource.db_c.url=jdbc:mysql://127.0.0.1:3306/test_C?autoReconnect=true&useUnicode=true&characterEncoding=utf8&logSlowQueries=false&slowQueryThresholdMillis=0&allowMultiQueries=true
datasource.db_c.username=root
datasource.db_c.password=123456


2、写一个ThreadLocalDynamicDataSource类继承AbstractRoutingDataSource,并实现determineCurrentLookupKey方法
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceTypeManager.get();
}
}
3、编写DataSourceTypeManager执行类,并利用ThreadLocal以空间换时间的方式解决线程安全问题
public class DataSourceTypeManager {
private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
@Override
protected DataSources initialValue() {
return DataSources.DATASOURCE_DEFAULT;
}
};

public static DataSources get() {
return dataSourceTypes.get();
}

public static void set(DataSources dataSourceType) {
dataSourceTypes.set(dataSourceType);
}

public static void reset() {
dataSourceTypes.set(DataSources.DATASOURCE_DEFAULT);
}

public static void clearDataSources () {
dataSourceTypes.remove();
}
}




上面代码利用initialValue方法,调用初始化时指定的DataSources 类型,这里DataSources 为枚举类型,里面定义三种数据源的名称,代码如下:
public enum DataSources {
DATASOURCE,DATASOURCE_DEFAULT,DATASOURCE_ORACLE
}

4、multipleDataSource多数据源配置的xml配置
<bean id="multipleDataSource"
class="com.hxqc.basic.dependency.datasource.ThreadLocalRountingDataSource">
<property name="defaultTargetDataSource" ref="ncDataSource"/>
<property name="targetDataSources">
<map key-type="com.hxqc.basic.dependency.datasource.DataSources">
<entry key="DATASOURCE" value-ref="erpDataSource"/>
<entry key="DATASOURCE_DEFAULT" value-ref="ncDataSource"/>
<entry key="DATASOURCE_ORACLE" value-ref="oracleDataSource"/>
</map>
</property>
</bean>



multipleDataSource多数据源配置的Java配置(上文已经贴出该配置,这里再强调一下)
@Bean
@Primary
public DataSource multipleDataSource(){
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(dataSourceDefault());

Map<Object,Object> dataSourcesType = new HashMap();
dataSourcesType.put(DataSources.DATASOURCE_A,dataSourceDefault());
dataSourcesType.put(DataSources.DATASOURCE_B,dataSourceB());
dataSourcesType.put(DataSources.DATASOURCE_C,dataSourceC());

dynamicDataSource.setTargetDataSources(dataSourcesType);

return dynamicDataSource;
}

5.在DAOImpl中切换数据源 或者配置aop切面根据不同的包下面的数据,采用不同的数据源



1,在Dao层直接切换数据源,如切换到默认数据源
DataSourceTypeManager.set(DataSources.DATASOURCE_DEFAULT)

2.根据aop进行动态切换
aop xml配置
<bean id="dataSourceInterceptor" class="com.hxqc.basic.dependency.interceptor.DataSourceInterceptor"/>
<aop:config>
<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
<aop:pointcut id="datasource" expression="execution(* com.hxqc.data.gather.core.erp.*.mapper..*.*(..))"/>
<aop:pointcut id="datasource_oracle" expression="execution(* com.hxqc.data.gather.core.oracle.*.mapper..*.*(..))"/>
<aop:pointcut id="datasource_default" expression="execution(* com.hxqc.data.gather.core.nc.*.mapper..*.*(..))"/>
<aop:before method="setDataSource" pointcut-ref="datasource"/>
<aop:before method="setDataSourceDefault" pointcut-ref="datasource_default"/>
<aop:before method="setDataSourceOracle" pointcut-ref="datasource_oracle"/>
</aop:aspect>
</aop:config>
aop java 配置
@Aspect
@Component
public class DataSourceAspect {

private Logger log = LoggerFactory.getLogger(getClass());

@Pointcut("execution(* com.may.dataA.mapper.*.*(..))")
public void pointCutA(){}

@Pointcut("execution(* com.may.dataB.mapper.*.*(..))")
public void pointCutB(){}

@Pointcut("execution(* com.may.dataC.mapper.*.*(..))")
public void pointCutC(){}
@Pointcut("execution(* com.may..*.*(..))")
public void annotation(){}



public void setDataSourceDefault(JoinPoint jp){
DataSourceManager.set(DataSources.DATASOURCE_A);
log.info("切换数据源","切换到默认数据源A");

}
public void setDataSourceB(JoinPoint jp){
DataSourceManager.set(DataSources.DATASOURCE_B);
log.info("切换数据源","切换到数据源B");
}

public void setDataSourceC(JoinPoint jp){
DataSourceManager.set(DataSources.DATASOURCE_C);
log.info("切换数据源","切换到数据源C");
}

@Before("@annotation(ds)")
public void setAnnotationDataSource(JoinPoint jp,DataSource ds){
// 获取当前的指定的数据源;
String dsId = ds.value();
// 如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。
/*if (!DataSourceManager.containsDataSource(dsId)) {

log.error("数据源[{}]不存在,使用默认数据源 > {}", ds.value(), jp.getSignature());

} else {

log.debug("Use DataSource : {} > {}", ds.value(), jp.getSignature());

// 找到的话,那么设置到动态数据源上下文中。
DynamicDataSourceContextHolder.setDataSourceType(ds.name());
}*/

//将string值转换为enum
DataSourceManager.set(Enum.valueOf(DataSources.class,ds.value()));

log.info("切换数据源"+ds.value(),"切换到默认数据源"+ds.value());
}



}

解释,这里定义三个切面,分别对应三个数据源,如在oracle包下的,动态切换到oracle数据源,上面配置的最后部分是用到了自定义注解DataSource注解,该注解在方法上,通过切面来进行数据源的切换,具体看源码

然后配置切面类
/**
*
*
* @author maple
*
*/
public class DataSourceInterceptor {

public void setDataSourceDefault(JoinPoint jp){
DataSourceTypeManager.set(DataSources.DATASOURCE_DEFAULT);
LogU.i("切换数据源","切换到NC数据源");
System.out.println(jp.getTarget().getClass().getSimpleName());
}

public void setDataSource(JoinPoint jp){
DataSourceTypeManager.set(DataSources.DATASOURCE);
LogU.i("切换数据源","切换到ERP数据源");
}

public void setDataSourceOracle(JoinPoint jp){
DataSourceTypeManager.set(DataSources.DATASOURCE_ORACLE);
LogU.i("切换数据源","切换到Oracle数据源");
}
}





`问题:`多数据源切换是成功了,但牵涉到事务呢?单数据源事务是ok的,但如果多数据源需要同时使用一个事务呢?这个问题有点头大,网络上有人提出用atomikos开源项目实现JTA分布式事务处理。你怎么看?

注意:

本文源码放在码云上,注意,源码将XML配置的内容都改成了JAVA配置,项目是基于Spring-boot,也提供了自定义注解来改变动态数据源!

本文源码:http://git.oschina.net/youjie1/may-dynamic-datasource
原文

 

SpringCloud进阶

Spring Clouditmuch.com 回复了问题 • 2 人关注 • 1 个回复 • 210 次浏览 • 2017-09-11 22:48 • 来自相关话题