AbstractRoutingDataSource動態數據源切換

操做數據通常都是在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);

    }

相關文章
相關標籤/搜索