Spring动态切换多数据源解决方案

spring的AbstractRoutingDataSource实现数据库链接切换。java

能够动态的切换数据源,可是对事务有影响,能够用JTA实现事务一致,可是效率较低。并且咱们项目事务能够单库一致就知足需求。因此采用了这种方式。mysql

下面是具体的实现过程:spring

1)spring的配置文件中配置多个数据源。sql

a:若是有相同的属性值可以下配置数据库

<!-- 数据源相同的内容 -->  
<bean  
        id="parentDataSource"  
        class="org.apache.commons.dbcp.BasicDataSource"  
        destroy-method="close">  
        <property  
            name="driverClassName"  
            value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />  
        <property name="username" value="sa" />  
        <property name="password" value="net2com" />  
</bean> 
<!-- start如下配置各个数据源的特性 -->  
<bean parent="parentDataSource" id="testDataSource">   
        <property name="url" value="jdbc:sqlserver://localhost:1433;databaseName=test" />  
</bean>   
<bean parent="parentDataSource" id="UserDataSource">   
            <property   name="url"  value="jdbc:sqlserver://localhost:1433;databaseName=User" />  
</bean>

b:若是没有,能够以下配置apache

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
destroy-method="close">  
<property name="driverClassName" value="com.mysql.jdbc.Driver" />  
<property name="url" value="${mysql.url}" />  
<property name="username" value="${mysql.username}" />  
<property name="password" value="${mysql.password}" />  
<!-- 链接池启动时的初始值 -->  
<property name="initialSize" value="1" />  
<!-- 最大空闲值.当通过一个高峰时间后,链接池能够慢慢将已经用不到的链接慢慢释放一部分,一直减小到maxIdle为止 -->  
<property name="maxIdle" value="2" />  
<!-- 最小空闲值.当空闲的链接数少于阀值时,链接池就会预申请一些链接,以免洪峰来时再申请而形成的性能开销 -->  
<property name="minIdle" value="1" />  
</bean>  
  
<bean id="xiaomi" class="org.apache.commons.dbcp.BasicDataSource"  
destroy-method="close">  
<property name="driverClassName" value="com.mysql.jdbc.Driver" />  
<property name="url" value="${mysql.url.xiaomi}" />  
<property name="username" value="${mysql.username}" />  
<property name="password" value="${mysql.password}" />  
<!-- 链接池启动时的初始值 -->  
<property name="initialSize" value="1" />  
<!-- 最大空闲值.当通过一个高峰时间后,链接池能够慢慢将已经用不到的链接慢慢释放一部分,一直减小到maxIdle为止 -->  
<property name="maxIdle" value="2" />  
<!-- 最小空闲值.当空闲的链接数少于阀值时,链接池就会预申请一些链接,以免洪峰来时再申请而形成的性能开销 -->  
<property name="minIdle" value="1" />  
</bean>

2)定义动态的数据源app

<bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource">  
    <property name="targetDataSources">   
       <map key-type="java.lang.String">   
           <entry value-ref="testDataSource" key="test"></entry>  
           <entry value-ref="UserDataSource" key="User"></entry>  
       </map>   
    </property>   
    <property name="defaultTargetDataSource" ref="testDataSource" ></property>  
</bean>

 在这个配置中第一个property属性配置目标数据源,<map key-type="Java.lang.String">中的key-type必需要和静态键值对照类DataSourceConst中的值的类型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必需要和静态键值对照类中的值相同,若是有多个值,能够配置多个< entry>标签。第二个property属性配置默认的数据源。ide

java code 以下:sqlserver

/** 
 * 动态配置多数据源 
 * 数据源的名称常量类 
 * @author LONGHUI_LUO 
 * 
 */  
public class DataSourceConst {  
    public static final String TEST="test";  
    public static final String USER="User";  
}
/** 
 * 得到和设置上下文环境 主要负责改变上下文数据源的名称 
 *  
 * @author LONGHUI_LUO 
 *  
 */  
public class DataSourceContextHolder {  
    private static final ThreadLocal contextHolder = new ThreadLocal(); // 线程本地环境  
  
    // 设置数据源类型  
    public static void setDataSourceType(String dataSourceType) {  
        contextHolder.set(dataSourceType);  
    }  
  
    // 获取数据源类型  
    public static String getDataSourceType() {  
        return (String) contextHolder.get();  
    }  
  
    // 清除数据源类型  
    public static void clearDataSourceType() {  
        contextHolder.remove();  
    }  
  
}

3)修改事务管理器的数据源为动态数据源,指定事务注解的排序为2,咱们会指定切换数据源的注解为1,这样在事务以前切换数据源,不然在事务以后切换的的话,无效。性能

<!-- 注解事务处理 -->  
<bean id="transactionManager"  
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
<property name="dataSource" ref="dynamicDataSource" />  
<!-- 启用注解 -->  
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>

 

4)定义切换数据库的注解和aop切面,指定排序为1,这里有个疑问,经过切点获取代理方法的注解数据,我用的是反射,可是网上有说能够直接做为参数传入的,我一直没有试验成功,不知道哪里有错,后续哪位大神指导的,能够分享一下。

@Target({ElementType.METHOD, ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Inherited  
@Documented  
public @interface DataSource {  
   String name();  
}  
  
@Component  
@Aspect  
@Order(1)  
public class DataSourceProxy {  
  
@Before(value="@annotation(com.futuren.wzk.common.datasource.DataSource)")  
public void before(JoinPoint jp) {  
    String methodName = jp.getSignature().getName();   
    Method[] methods = jp.getTarget().getClass().getMethods();  
    for(Method method : methods) {  
        if(method.getName().equals(methodName)) {  
        DataSource ds = method.getAnnotation(DataSource.class);  
        DataSourceContextHolder.setDataSourceType(ds.name());  
     }  
   }  
 }  
}

在项目中使用

@Override  
@Transactional  
@DataSource(name="ucenter")  
public int addUser(User user) {  
userMapper.insert(user);  
return user.getUid();  
}

这种方法,只支持单库事务,若是要多库事务,可能要引入JTA,或者是其余自定义实现。或者其余我不知道的技术。欢迎讨论!

相关文章
相关标签/搜索