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