操做數據通常都是在DAO層進行處理,能夠選擇直接使用JDBC進行編程(http://blog.csdn.net/yanzi1225627/article/details/26950615/)java
或者是使用多個DataSource 而後建立多個SessionFactory,在使用Dao層的時候經過不一樣的SessionFactory進行處理,不過這樣的入侵性比較明顯,通常的狀況下咱們都是使用繼承HibernateSupportDao進行封裝了的處理,若是多個SessionFactory這樣處理就是比較的麻煩了,修改的地方估計也是蠻多的mysql
最後一個,也就是使用AbstractRoutingDataSource的實現類經過AOP或者手動處理實現動態的使用咱們的數據源,這樣的入侵性較低,很是好的知足使用的需求。好比咱們但願對於讀寫分離或者其餘的數據同步的業務場景spring
下面看看圖片 sql
單數據源的場景(通常的Web項目工程這樣配置進行處理,就已經比較可以知足咱們的業務需求)數據庫
多數據源多SessionFactory這樣的場景,估計做爲剛剛開始想象想處理在使用框架的狀況下處理業務,配置多個SessionFactory,而後在Dao層中對於特定的請求,經過特定的SessionFactory便可處理實現這樣的業務需求,不過這樣的處理帶來了不少的不便之處,全部不少狀況下咱們寧願直接使用封裝的JDBC編程,或者使用Mybatis處理這樣的業務場景編程
使用AbstractRoutingDataSource 的實現類,進行靈活的切換,能夠經過AOP或者手動編程設置當前的DataSource,不用修改咱們編寫的對於繼承HibernateSupportDao的實現類的修改,這樣的編寫方式比較好,至於其中的實現原理,讓我細細到來。咱們想看看如何去應用,實現原理慢慢的說!mybatis
編寫AbstractRoutingDataSource的實現類,HandlerDataSource就是提供給咱們動態選擇數據源的數據的信息,咱們這裏編寫一個根據當前線程來選擇數據源,而後經過AOP攔截特定的註解,設置當前的數據源信息,也能夠手動的設置當前的數據源,在編程的類中。框架
package com.common.utils.manydatasource;ide
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;函數
/**
* descrption: 多數據源的選擇
* authohr: wangji
* date: 2017-08-21 10:32
*/
public class MultipleDataSourceToChoose extends AbstractRoutingDataSource {
/**
* @desction: 根據Key獲取數據源的信息,上層抽象函數的鉤子
* @author: wangji
* @date: 2017/8/21
* @param:
* @return:
*/
@Override
protected Object determineCurrentLookupKey() {
return HandlerDataSource.getDataSource();
}
}
設置動態選擇的Datasource,這裏的Set方法能夠留給AOP調用,或者留給咱們的具體的Dao層或者Service層中手動調用,在執行SQL語句以前。
package com.common.utils.manydatasource;
/**
* descrption: 根據當前線程來選擇具體的數據源
* authohr: wangji
* date: 2017-08-21 10:36
*/
public class HandlerDataSource {
private static ThreadLocal<String> handlerThredLocal = new ThreadLocal<String>();
/**
* @desction: 提供給AOP去設置當前的線程的數據源的信息
* @author: wangji
* @date: 2017/8/21
* @param: [datasource]
* @return: void
*/
public static void putDataSource(String datasource) {
handlerThredLocal.set(datasource);
}
/**
* @desction: 提供給AbstractRoutingDataSource的實現類,經過key選擇數據源
* @author: wangji
* @date: 2017/8/21
* @param: []
* @return: java.lang.String
*/
public static String getDataSource() {
return handlerThredLocal.get();
}
/**
* @desction: 使用默認的數據源
*/
public static void clear() {
handlerThredLocal.remove();
}
}
設置攔截數據源的註解,能夠設置在具體的類上,或者在具體的方法上,dataSource是當前數據源的一個別名用於標識咱們的數據源的信息。
package com.common.utils.manydatasource;
import java.lang.annotation.*;
/**
* @description: 建立攔截設置數據源的註解
* Created by wangji on 2017/8/21.
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicSwitchDataSource {
String dataSource() default "";
}
AOP攔截類的實現,經過攔截上面的註解,在其執行以前處理設置當前執行SQL的數據源的信息,HandlerDataSource.putDataSource(….),這裏的數據源信息從咱們設置的註解上面獲取信息,若是沒有設置就是用默認的數據源的信息。
package com.common.utils.manydatasource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* descrption: 使用AOP攔截特定的註解去動態的切換數據源
* authohr: wangji
* date: 2017-08-21 10:42
*/
@Aspect
@Slf4j
@Component
@Order(1)
public class HandlerDataSourceAop {
//@within在類上設置
//@annotation在方法上進行設置
@Pointcut("@within(com.common.utils.manydatasource.DynamicSwitchDataSource)||@annotation(com.common.utils.manydatasource.DynamicSwitchDataSource)")
public void pointcut() {}
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint)
{
Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();
DynamicSwitchDataSource annotationClass = method.getAnnotation(DynamicSwitchDataSource.class);//獲取方法上的註解
if(annotationClass == null){
annotationClass = joinPoint.getTarget().getClass().getAnnotation(DynamicSwitchDataSource.class);//獲取類上面的註解
if(annotationClass == null) return;
}
//獲取註解上的數據源的值的信息
String dataSourceKey = annotationClass.dataSource();
if(dataSourceKey !=null){
//給當前的執行SQL的操做設置特殊的數據源的信息
HandlerDataSource.putDataSource(dataSourceKey);
}
log.info("AOP動態切換數據源,className"+joinPoint.getTarget().getClass().getName()+"methodName"+method.getName()+";dataSourceKey:"+dataSourceKey==""?"默認數據源":dataSourceKey);
}
@After("pointcut()")
public void after(JoinPoint point) {
//清理掉當前設置的數據源,讓默認的數據源不受影響
HandlerDataSource.clear();
}
}
配置數據源在Spring 核心容器中配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis
jdbc.username=root
jdbc.password=root
jdbc2.url=jdbc:mysql://127.0.0.1:3306/datasource2
<!-- 配置數據源 -->
<bean id="dataSource0" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
</bean>
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc2.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
</bean>
配置以前咱們實現的數據源選擇的中間層AbstractRoutingDataSource的實現類,這裏的key就是數據源信息的別名,經過這個key能夠選擇到數據源的信息。MultipleDataSourceToChoose就是上面寫的數據源選擇器的實現類
bean id="dataSource" class="com.common.utils.manydatasource.MultipleDataSourceToChoose" lazy-init="true">
<description>數據源</description>
<property name="targetDataSources">
<map key-type="java.lang.String" value-type="javax.sql.DataSource">
<entry key="datasource0" value-ref="dataSource0" />
<entry key="datasource1" value-ref="dataSource1" />
</map>
</property>
<!-- 設置默認的目標數據源 -->
<property name="defaultTargetDataSource" ref="dataSource0" />
</bean>
SessionFactory的配置仍是照舊,使用之前的配置,只不過當前選擇的數據源是datasource,也就是數據源選擇的中間層MultipleDataSourceToChoose,由於當前的中間層中實現了DataSource這個接口,因此能夠看作爲DataSource的是實現類啦,因此配置不會出現問題。
簡單的使用AOP進行測試一下,這裏測試的結果時不一樣的,因此是生效的,使用了不一樣的數據源,可是底層的實現沒有進行任何的修改處理。
@Service
@Slf4j
public class UserInfoService implements IUserInfoService {
@Resource
private UserDao userDao;
@Autowired
private CommonHibernateDao commonDao;
@TestValidateParam
public User getUserInfoById(Integer id) {
return userDao.findById(id);
}
@DynamicSwitchDataSource(dataSource = "datasource0")
public void save(User user) {
userDao.save(user);
}
@DynamicSwitchDataSource(dataSource = "datasource1")
public List<User> findAll(){
String sql = "select u.userName as name,u.userAge as age,u.userAddress as address,u.id from user u";
List<User> list =commonDao.findListBySQL(sql,User.class);
return list;
}
}
也能夠不適用AOP,直接在編程中實現,經過測試,結果分別爲兩個數據庫中的信息
public void test(){
HandlerDataSource.putDataSource("datasource1");
String sql = "select u.userName as name,u.userAge as age,u.userAddress as address,u.id from user u";
List<User> list =commonDao.findListBySQL(sql,User.class);
HandlerDataSource.putDataSource("datasource0");
commonDao.deleteById("2",User.class);
}