1. 程式人生 > >基於spring實現多資料來源通過註解切換

基於spring實現多資料來源通過註解切換

主要使用spring的AbstractRoutingDataSource實現,先簡單瞭解下AbstractRoutingDataSource

我們配置的多個數據源會放在AbstractRoutingDataSource的 targetDataSources和defaultTargetDataSource中

AbstractRoutingDataSourcegetConnection方法會先呼叫determineTargetDataSource()方法返回lookupkey鍵,根據lookupkey鍵對不同目標資料來源呼叫。

所以,我們主要通過自己重寫determineCurrentLookupKey()方法返回lookupKey即可。

實現多資料來源切換的大致思路是:

  1. 建立用於標誌資料來源的自定義註解
  2. 通過配置切面,在操作資料庫的方法之前,掃描該方法的註解所配置的資料來源名稱,將名稱儲存在一個代表當前執行緒變數工具類
  3. 建立AbstarctRoutingDataSource子類,重寫determineCurrentLookupKey()方法,把當前執行緒變數的工具類儲存的資料來源名稱返回即可。
  4. 在spring配置檔案中,將多個數據源配置到我們建立的DynamicDataSourc

第一步,配置資料來源

<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="username" value="root" />
   <property name="password" value="spring" />
   <property name="url" value="jdbc:mysql://localhost:3306/taotao?characterEncoding=utf-8" />
   <!-- 最大併發連線數 -->
   <property name="maxActive" value="30" />
   <!-- 最小空閒連線數 -->
   <property name="minIdle" value="5" />
   <!-- 用於顯示資料來源監控中的sql語句監控 -->
   <property name="filters" value="stat" />
</bean>

<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="username" value="root" />
   <property name="password" value="spring" />
   <property name="url" value="jdbc:mysql://localhost:3306/taobao?characterEncoding=utf-8" />
   <!-- 最大併發連線數 -->
   <property name="maxActive" value="30" />
   <!-- 最小空閒連線數 -->
   <property name="minIdle" value="5" />
   <!-- 用於顯示資料來源監控中的sql語句監控 -->
   <property name="filters" value="stat" />
</bean>

第二步,定義用來切庫的註解,和列舉類

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Dataswitch {
    Datatype value() default Datatype.master;
}
public enum Datatype {
    master("masterDataSource"),slave("slaveDataSource");
    private String value;
    Datatype(String name){
        this.value = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

第三步,定義一個當前執行緒的變數的工具類,用於設定對應的資料來源名稱

public  class DynamicDataSourceHolder {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    public static String getThreadLocal() {
        return threadLocal.get();
    }

    public static void setThreadLocal(String name) {
        threadLocal.set(name);
    }
    public static void clear(){
        threadLocal.remove();
    }
}

第四步,建立AbstactRoutingDataSource的子類,重寫determineCurrentLockupKey方法

public class DynamicDataSource extends AbstractRoutingDataSource{
    protected Object determineCurrentLookupKey() {
        System.out.println(DynamicDataSourceHolder.getThreadLocal());
        return DynamicDataSourceHolder.getThreadLocal();
    }
}

第五步,將多資料來源配置到用我們建立的DynamicDataSource

<bean id="dynamicDataSource" class="xin.youhuila.sorceswitch.process.DynamicDataSource">
   <property name="targetDataSources">
      <map key-type="java.lang.String">
         <entry key="masterDataSource" value-ref="masterDataSource"></entry>
         <entry key="slaveDataSource" value-ref="slaveDataSource"></entry>
      </map>
   </property>
   <property name="defaultTargetDataSource" ref="masterDataSource"></property>
</bean>

第六步,配置切面,在操作資料庫方法之前,獲取註解配置的資料來源名稱,返回

@Component
@Aspect
@Order(0)
public class DataSourceAspect {
    @Pointcut("execution (* xin.youhuila.sorceswitch.service..*(..))")
    public void aspect(){

    }
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        Class<?> clazz = joinPoint.getTarget().getClass();
        Method[] method = clazz.getMethods();
        Dataswitch dataswitch = null;
        boolean is = false;
        for(Method m:method){
            if(m.isAnnotationPresent(Dataswitch.class)){
                dataswitch = m.getAnnotation(Dataswitch.class);
                DynamicDataSourceHolder.setThreadLocal(dataswitch.value().getValue());
                is = true;
            }
        }
        if(!is){
            DynamicDataSourceHolder.setThreadLocal(Datatype.master.getValue());
        }
    }
    @After("aspect()")
    public void after(){
        DynamicDataSourceHolder.clear();
    }
}