spring + mybatis + 多數據源整合事務

一、核心思想,spring提供了一個DataSource的子類,該類支持多個數據源java

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

該類的源碼以下:mysql

org.springframework.jdbc.datasource.lookup;

java.sql.Connection;
java.sql.SQLException;
java.util.HashMap;
java.util.Iterator;
java.util.Map;
java.util.Map.Entry;
javax.sql.DataSource;
org.springframework.beans.factory.InitializingBean;
org.springframework.jdbc.datasource.AbstractDataSource;
org.springframework.jdbc.datasource.lookup.DataSourceLookup;
org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
org.springframework.util.Assert;

AbstractRoutingDataSource AbstractDataSource InitializingBean {

    // 注意這裏,這裏使用Map存放多個數據源
    Map<Object, Object> targetDataSources;
 
    ... // 篇幅有限,省略其餘代碼,有興趣的同窗能夠自行研究,仍是支持蠻多功能的,好比JNDI等數據源方式     

    // 注意這個暴露的方法,spring在從數據源中獲取數據庫鏈接時,經過這個方法肯定數據源
    DataSource determineTargetDataSource() {
        Assert.notNull(.resolvedDataSources, );
        Object lookupKey = .determineCurrentLookupKey();
        DataSource dataSource = (DataSource).resolvedDataSources.get(lookupKey);
        (dataSource == && (.lenientFallback || lookupKey == )) {
            dataSource = .resolvedDefaultDataSource;
        }

        (dataSource == ) {
            IllegalStateException(+ lookupKey + );
        } {
            dataSource;
        }
    }

    // 對外暴露的決定數據源的方法,經過外部設置數據源的key值,
    // spring自行從已存儲的數據源中查找指定數據源
    Object determineCurrentLookupKey();
}

經過分析源碼,能夠知道咱們只須要實現如何動態設置切換數據源的方式便可,能夠考慮使用註解的方式在指定的位置添加數據源註解,利用AOP動態指定數據源。程序員

自定義的數據源以下:spring

com.yao.yz.yaowangdrug.dataSource;

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

DynamicDataSource AbstractRoutingDataSource{

    Object determineCurrentLookupKey() {
        DataSourceHolder.();
    }
}


二、自定義註解,本章節不作過多解釋,對於註解有興趣的同窗請自行研究,廢話一句:程序員以實際應用爲主,研究行的內容請自行學習sql

com.yao.yz.yaowangdrug.dataSource;

java.lang.annotation.*;

(RetentionPolicy.)
(ElementType.)
@{
    String value();
}


三、定義一個數據源切換功能組件,其中數據源存放的方式採用ThreadLocal的形式,我是單獨把存放作成一個額外的組件,這個是能夠自行決定的,代碼以下:數據庫

com.yao.yz.yaowangdrug.dataSource;

DataSourceHolder {

    ThreadLocal<String> = InheritableThreadLocal<String>();

    setDataSourceKey(String dataSource) {
        .set(dataSource);
    }

    Object getDataSourceKey() {
       .get();
    }

}
com.yao.yz.yaowangdrug.dataSource;

org.apache.log4j.Logger;
org.aspectj.lang.JoinPoint;
org.aspectj.lang.reflect.MethodSignature;

java.lang.reflect.Method;

DynamicAspect {

    Logger = Logger.(DynamicAspect.);

    switchDataSource(JoinPoint point) NoSuchMethodException
    {
        Object target = point.getTarget();
        String method = point.getSignature().getName();

        Class<?>[] classz = target.getClass().getInterfaces();

        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        Method m = classz[].getMethod(method, parameterTypes);
        (m != && m.isAnnotationPresent(.)) {
            data = m
                    .getAnnotation(.);
            DataSourceHolder.(data.value());
        }
        .info(+ DataSourceHolder.());
    }

}


四、在spring配置文件添加多個數據源,並將多個數據源統一歸入自定義數據源的管理,具體的配置信息以下:express

<!--裝載配置文件,將數據源的配置作成配置文件,方便管理,有興趣的同窗自行參考-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="Config.properties"/>
</bean>

<!--數據源1-->
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="${dataSource1.jdbc.url}" />
<property name="user" value="${dataSource1.jdbc.username}" />
<property name="password" value="${dataSource1.jdbc.password}" />
<property name="minPoolSize" value="${dataSource1.jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${dataSource1.jdbc.maxPoolSize}" />
</bean>

<!--數據源2-->
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="${dataSource2.jdbc.url}" />
<property name="user" value="${dataSource2.jdbc.username}" />
<property name="password" value="${dataSource2.jdbc.password}" />
<property name="minPoolSize" value="${dataSource2.jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${dataSource2.jdbc.maxPoolSize}" />
</bean>

<!--自定義數據源,將全部的數據源歸入自定義數據源管理-->
<bean id="dataSource" class="com.yao.yz.yaowangdrug.dataSource.DynamicDataSource">
<property name="targetDataSources">
<map>
         <!-- 對應spring提供的AbstractRoutingDataSource的Map -->

<entry key="dataSource1" value-ref="dataSource1"/>
<entry key="dataSource2" value-ref="dataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource1"/>
</bean>


五、配置數據源切換的AOP,代碼以下:apache

<!--動態決定數據源-->
<bean id="dataSourceSwitchAspect" class="com.yao.yz.yaowangdrug.dataSource.DynamicAspect"/>
<aop:config>
<aop:aspect id="dynamicAspect" ref="dataSourceSwitchAspect">
<!--數據源切換能夠控制爲Dao或者service層,請根據實際業務須要自行決定-->
     <aop:pointcut id="dynamicPointCut" expression="execution(* com.xx.xx.xx.dao.*.*(..))"/>
<aop:before method="switchDataSource" pointcut-ref="dynamicPointCut"/>
</aop:aspect>
</aop:config>


六、使用spring整合mybatis,和通常的整合卻別在於使用的數據源爲自定義數據源,代碼以下:mybatis

   
      
         classpath:ModelMapper.xml
         classpath:ModelMapper1.xml
         classpath:ModelMapper2.xml

注意:目前精力有限,此處爲何可使用自定義多個數據源的底層原理還沒來得及看,有興趣的同窗請自行研究,若是好心的話能夠簡單的告知我,謝謝你了~~~app


七、將自定義數據源歸入spring的事務管理器管理,配置代碼以下:

<!--事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--申明事務回滾的方法和異常信息-->
<tx:advice id="txAdvic" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="do*" read-only="false" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<!--申明事務範圍-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.xx.xx.xx.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvic" pointcut-ref="txPointCut"/>
</aop:config>


Model類:

com.yao.yz.yaowangdrug.model;

DBModel {

    String ;

    String ;

    String getValue() {
        ;
    }

    setValue(String value) {
        .= value;
    }

    String getKey() {
        ;
    }

    setKey(String key) {
        .= key;
    }
}

Dao接口:

com.yao.yz.yaowangdrug.dao;

;
com.yao.yz.yaowangdrug.model.DBModel;

DBDao {

    ()
    insert(DBModel dbModel) Exception;

}

Service接口:

com.yao.yz.yaowangdrug.service;

com.yao.yz.yaowangdrug.model.DBModel;
;

DBService {

    doInsert() Exception;

    doUpdate() Exception;

    testSeperateDB() Exception;

}


關於事務的控制,有一下幾點說明:

一、採用申明或者註解實現事務控制時時,由於開啓了事務控制,因此若是是兩個不一樣的數據源Dao,根據spring的事務傳播特性,第二個事務開啓將使用已有的事務(即將採用第一個數據源的數據庫鏈接)進行事務操做,因此此時事務控制是失效的(即便切面執行了數據源切換)。結論就是跨數據庫的事務是沒法經過spring的數據庫控制實現的!!!請切記。


二、同一個數據源的事務控制和普通的數據源控制是一致的,沒有什麼區別。

以上代碼都是通過測試經過,能夠實現跨庫的數據方式,主要的應用場景是mysql的數據庫讀寫分離。若是不正確的地方請告知,謝謝!

相關文章
相關標籤/搜索