數據源的無縫切換

隨着業務的發展,用戶量的增大,單表單庫已經知足不了咱們的業務需求,所以須要分庫分表,可是隨之帶來的問題是當咱們須要從庫中撈取某些數據的時候,常常須要在多個數據庫來回切換,如何作到無縫切換,這裏主要經過配置數據源,自定義註解和aop技術來解決。主要包含如下4步:java

一、切換數據源spring

例如咱們有兩個數據源,一個是ds0,一個是ds1,咱們能夠經過以下方式配置一個動態數據源。咱們只須要自定義一個類繼承AbstractRoutingDatasource,實現determineCurrentLookupKey方法,這個方法能夠決定咱們每次使用的數據源。contextHolder則是一個線程局部變量,保證線程的安全。也就是隻要每次在準備切換數據庫的時候,經過contextHolder.setDatabaseName就能夠切換數據源了。數據庫

/**
 * 功能:數據庫切換工具類
 * 版本:1.0
 * 日期:2016/12/2 13:44
 * 做者:屠蘇
 */
public class DataSourceContextHolder {
    // 線程局部變量,用於保存保存數據庫的名稱
    private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();

    public static void setDataBaseName(String dataBaseName) {
        contextHolder.set(dataBaseName);
    }

    public static String getDataBaseName() {
        return contextHolder.get();
    }

    public static void clearDataBaseName() {
        contextHolder.remove();
    }
}
/**
 * 功能:動態數據源
 * 版本:1.0
 * 日期:2016/12/2 13:49
 * 做者:屠蘇
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    // 返回數據源的key
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataBaseName();
    }
}
<!--配置動態數據源-->
<bean id="dataSource" class="com.netease.payment.util.datasource.DynamicDataSource">
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <entry key="ds0" value-ref="dataSource0"/>
            <entry key="ds1" value-ref="dataSource1"/>
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="dataSource0"/>
</bean>

二、自定義註解安全

首先作一個自定義註解,而後每次在須要切數據源的方法上加上@Datasource("xx")就能夠了。ide

/**
 * 描述: 自定義數據源註解
 * 版本號: 1.0
 * 日期: 2017/2/21 11:49
 * 做者: 屠蘇
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Datasource {
    String value() default "ds0";
}

三、Aop工具

首先定義一個切入點,對全部存在@Datasource的註釋的方法進行切面,而後獲取簽名方法,獲取方法上的註釋,替換數據源。spa

package com.elin4it.ssm.aop;

import com.elin4it.ssm.annotation.Datasource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 功能:切換數據源aop
 * 版本:1.0
 * 日期:2016/12/29 14:18
 * 做者:屠蘇
 */
@Aspect
@Component
public class DsAop {

    /**
     * 第一個 * 表明全部的返回值類型
     * 第二個 * 表明全部的類
     * 第三個 * 表明類全部方法
     * 最後一個 .. 表明全部的參數
     */
    @Pointcut("@annotation(com.elin4it.ssm.annotation.Datasource)")
    private void anyMethod(){}//定義一個切入點

    @Before("anyMethod()")
    public void before(JoinPoint joinPoint){
        Object target = joinPoint.getTarget();
        Class<?> classObj = target.getClass();

        // 獲取鏈接點的簽名方法對象
        Signature signature = joinPoint.getSignature();
        System.out.println("signature method=" + signature.getName());

        // 方法名
        String methodName = joinPoint.getSignature().getName();

        // 方法參數類型
        Class<?>[] paraClasses = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();

        try {
            Method method = classObj.getMethod(methodName, paraClasses);

            if(method != null && method.isAnnotationPresent(Datasource.class)) {
                Datasource dataSource = method.getAnnotation(Datasource.class);

                /**
                 * 切換數據源 !!!!!!!
                 */
                DataSourceContextHolder.setDataBaseName(dataSource.value());

                System.out.println("value=" + dataSource.value());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @After("anyMethod()")
    public void after(){}

    @AfterThrowing("anyMethod()")
    public void exception(){}

    @Around("anyMethod()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        Object object = pjp.proceed();//執行該方法
        return object;
    }
}

四、調用切換數據源的方法.net

@Datasource("ds1")
public void testSwitchDS() {
    System.out.println("調用testSwitchDS方法!");
}
相關文章
相關標籤/搜索