隨着業務的發展,用戶量的增大,單表單庫已經知足不了咱們的業務需求,所以須要分庫分表,可是隨之帶來的問題是當咱們須要從庫中撈取某些數據的時候,常常須要在多個數據庫來回切換,如何作到無縫切換,這裏主要經過配置數據源,自定義註解和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方法!"); }