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,或者是其餘自定義實現。或者其餘我不知道的技術。歡迎討論!

相關文章
相關標籤/搜索