1.AbstractRoutingDataSource實作方式
這種實作方式網上可以找到很多介紹。大同小異,我這裡在總結一下。
1.1首先是資料源的配置
<!-- 有幾個資料源就配幾個 -->
<!-- 資料源配置1 -->
<bean id="testDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${unity.db.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
...等等配置資訊...
</bean>
<!-- 資料源配置2 -->
<bean id="testDataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${pub.db.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
...等等配置資訊...
</bean>
<!-- 這裡指定實作的動态資料源,配置預設資料源,配置key和對應資料源的對應關系 -->
<bean id="dataSource" class="com.xxx.dao.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="testDataSource1"/>
<entry key="dspinsight" value-ref="testDataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="master"/>
</bean>
1.2相關類的代碼
動态資料源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
存儲目前線程的資料源的類
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* @Description: 設定資料源類型
* @param dataSourceType 資料庫類型
* @return void
* @throws
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
/**
* @Description: 擷取資料源類型
* @param
* @return String
* @throws
*/
public static String getDataSourceType() {
return contextHolder.get();
}
/**
* @Description: 清除資料源類型
* @param
* @return void
* @throws
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
在方法上指定資料源的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
切面的實作方式1,這種實作方式有個問題,就是方法中多次切換資料源會有問題。
@Component
@Aspect
@Order(1)
public class DataSourceAspect{
private static Logger logger = LogFactory.log;
// 這裡是你的切面
@Pointcut("execution(public * com.xxx.ad.service..*.*(..))")
private void methodExec() {
}
@Before("methodExec()")
private void before(JoinPoint point) throws NoSuchMethodException, SecurityException {
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<? extends Object> classz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
Method m = classz.getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
String ds = data.value();
logger.info("exchange to data source :" + ds);
DynamicDataSourceHolder.setDataSource(ds);
}
}
@After("methodExec()")
private void after(JoinPoint point) {
DynamicDataSourceHolder.clearDataSource();
}
}
切面實作方式2,一個方法中可以切換多次資料源
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {
// 這裡的
@Around("@annotation(targetDataSource)")
public Object Around(ProceedingJoinPoint pjp, DataSource targetDataSource) throws Throwable {
String lastDbType = DataSourceContextHolder.getDBType();
long tid = Thread.currentThread().getId();
String methodName = "";
Signature signature = pjp.getSignature();
if(signature != null){
methodName = signature.getName();
}
LogFactory.sailInfo("切換資料源,lastDbType:{},tid:{},methodName:{}", lastDbType,tid,methodName);
String dataSourceKey = targetDataSource.value();
DataSourceContextHolder.setDBType(dataSourceKey);
LogFactory.sailInfo("設定資料源為 {},tid:{},methodName:{}", dataSourceKey,tid,methodName);
Object ret = null;
try {
ret = pjp.proceed();
}finally {
LogFactory.sailInfo("執行方法完畢,目前資料源:{},tid:{},methodName:{}", DataSourceContextHolder.getDBType(),tid,methodName);
LogFactory.sailInfo("恢複上次資料源:{},tid:{},methodName:{}", lastDbType,tid,methodName);
DataSourceContextHolder.setDBType(lastDbType);
}
return ret;
}
}
1.3使用方式
public class TestService ....{
@DataSource("master")
public void test1(){
如果使用切面方式2實作,方法裡面可以調用其他的資料庫操作的服務
testservice2.test2(); // 比如這個方法是操作其他的資料庫
}
}
2.使用中遇到的問題
2.1首先就是一個服務中存在多次切庫的情況。按照切面1的實作方式,是存在問題的。如果使用around切面的話,可以解決這個問題。個人覺得事務可以加在Mapper上或者Dao上,不要加在Service。如果需要,在考慮加在Service。
2.2事務問題。在一個事務中,無法進行切庫。原因的話,這裡不解釋了,網上很多介紹。解決辦法的話,比較多的是使用了jta,我也沒用過。大家搜尋分布式事務吧。。。
2.3切庫失敗的問題。其切庫失敗的原因肯定不止一種,多數情況下都是由于事務造成的。參考2.2。我個人使用過程中發現過一個現象,就是程式運作一段時間之後,切庫就莫名其妙的會失敗。經過長時間的排查發現,是有同僚的代碼中手動開啟了事務,但是事務會存在不送出的情況。。。還是和事務有關。
3.另一種實作方式參考
手動裝配Mapper,參考我的另一篇博文
https://blog.csdn.net/lizhengwei1989/article/details/88081917