spring+springMVC+Mybatis架構下采用AbstractRoutingDataSource、atomikos、JTA實現多數據源靈活切換以及分佈式事務管理

背景:html

  一、系統採用SSM架構、須要在10多個MYSQL數據庫之間進行切換並對數據進行操做,上篇博文《springMVC+Mybatis(使用AbstractRoutingDataSource實現多數據源切換時)事務管理未生效的解決辦法》java

 

  二、第一步先經過AbstractRoutingDataSource實現了多數據源的靈活切換,可是後來發現事務不起做用;mysql

  三、發現問題是由於重複掃描service包致使第二次掃入容器的BEAN沒有歸入事務管理,所以在springMVC的配置文件中排除了對Service註解的掃描,防止重複掃描,事務生效了,測試又發現數據源不能成功切換了;web

  四、查閱資料發現是因爲spring默認提供的事務管理DataSourceTransactionManager只能管理一個數據源的事務,所以考慮使用atomikos+JTA進行分佈式事務管理spring

 

配置文件:sql

  一、jdbc.properties數據庫

  配置你須要鏈接的數據庫資源express

jdbc_zhs.driverClassName=com.mysql.jdbc.Driver
jdbc_zhs.url.spider=jdbc:mysql://127.0.0.1:3306/fms?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_zhs.username=root
jdbc_zhs.password=root

jdbc_jcs.driverClassName=com.mysql.jdbc.Driver
jdbc_jcs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-jcs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_jcs.username=root
jdbc_jcs.password=root

jdbc_hks.driverClassName=com.mysql.jdbc.Driver
jdbc_hks.url.spider=jdbc:mysql://127.0.0.1:3306/fms-hks?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_hks.username=root
jdbc_hks.password=root

jdbc_xts.driverClassName=com.mysql.jdbc.Driver
jdbc_xts.url.spider=jdbc:mysql://127.0.0.1:3306/fms-xts?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_xts.username=root
jdbc_xts.password=root

jdbc_xxgcs.driverClassName=com.mysql.jdbc.Driver
jdbc_xxgcs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-xxgcs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_xxgcs.username=root
jdbc_xxgcs.password=root

jdbc_zdhs.driverClassName=com.mysql.jdbc.Driver
jdbc_zdhs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-zdhs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_zdhs.username=root
jdbc_zdhs.password=root

jdbc_gfs.driverClassName=com.mysql.jdbc.Driver
jdbc_gfs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-gfs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_gfs.username=root
jdbc_gfs.password=root

jdbc_kxs.driverClassName=com.mysql.jdbc.Driver
jdbc_kxs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-kxs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_kxs.username=root
jdbc_kxs.password=root

jdbc_fyd.driverClassName=com.mysql.jdbc.Driver
jdbc_fyd.url.spider=jdbc:mysql://127.0.0.1:3306/fms-fyd?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_fyd.username=root
jdbc_fyd.password=root

jdbc_ybj.driverClassName=com.mysql.jdbc.Driver
jdbc_ybj.url.spider=jdbc:mysql://127.0.0.1:3306/fms-ybj?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_ybj.username=root
jdbc_ybj.password=root

jdbc_yzh.driverClassName=com.mysql.jdbc.Driver
jdbc_yzh.url.spider=jdbc:mysql://127.0.0.1:3306/fms-yzh?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_yzh.username=root
jdbc_yzh.password=root

  二、applicationContext-common.xml:apache

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx  
        http://www.springframework.org/schema/tx/spring-tx.xsd 
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 啓動掃描 -->
    <context:component-scan base-package="com.fms;com.job;com.jmda;">
        <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> -->
    </context:component-scan>
    
    <!-- 文件上傳 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10240000"/>
        <property name="maxInMemorySize" value="10240000" />
    </bean>

    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPaths">
            <list>
                <value>/WEB-INF/pages/</value>
                <value>/WEB-INF/template/</value>
                <value>classpath:/jmda-ftl/</value>
            </list>
        </property>
        <property name="freemarkerSettings">
            <props>
                <prop key="template_update_delay">0</prop>
                <prop key="default_encoding">UTF-8</prop>
                <prop key="number_format">0.##########</prop>
                <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
                <prop key="classic_compatible">true</prop>
                <prop key="template_exception_handler">ignore</prop>
            </props>
        </property>
    </bean>
    
    <!-- 啓用CGliB -->
    <aop:aspectj-autoproxy  />
    
    <!-- 配置c3p0數據源,項目中有代碼須要C3P0數據源支持時額外配置,不須要能夠不配置 -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="jdbcUrl">
        <value><![CDATA[jdbc:mysql://localhost:3306/fms-zhs?useUnicode=yes&characterEncoding=UTF8]]></value> 
        </property>
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="user" value="root" />
        <property name="password" value="root" />
        <property name="maxPoolSize" value="200" />
        <property name="minPoolSize" value="1" />
        <property name="initialPoolSize" value="1" />
        <property name="maxIdleTime" value="30" />
        <property name="acquireIncrement" value="5" />
        <property name="maxStatements" value="0" />
        <property name="idleConnectionTestPeriod" value="60" />
        <property name="acquireRetryAttempts" value="30" />
        <property name="breakAfterAcquireFailure" value="true" />
        <property name="testConnectionOnCheckout" value="false" />
    </bean>
    
    <!-- 注入數據源鏈接配置文件 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath*:/spring/jdbc.properties</value>
            </list>
        </property>
    </bean>

    <!-- 多個數據源的公用配置,方便下面直接引用 -->
     <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
             destroy-method="close" abstract="true">
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
        <property name="poolSize" value="10" />
        <property name="minPoolSize" value="10"/>
        <property name="maxPoolSize" value="30"/>
        <property name="borrowConnectionTimeout" value="60"/>
        <property name="reapTimeout" value="20"/>
        <!-- 最大空閒時間 -->
        <property name="maxIdleTime" value="60"/>
        <property name="maintenanceInterval" value="60" />
        <property name="loginTimeout" value="60"/>
        <property name="logWriter" value="60"/>
        <property name="testQuery">
            <value>select 1</value>
        </property>
        
    </bean> 
    
    <!-- 下面是全部須要切換的數據源 -->
    <!-- 綜合所數據源 -->
    <bean id="ds_zhs" parent="abstractXADataSource">
    <!-- value只要多個XA數據源之間不重複就行,隨便取名 -->
        <property name="uniqueResourceName" value="mysql/zhs" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="URL">${jdbc_zhs.url.spider}</prop>
                <prop key="user">${jdbc_zhs.username}</prop>
                <prop key="password">${jdbc_zhs.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 艦船所數據源-->
     <bean id="ds_jcs" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/jcs" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_jcs.url.spider}</prop>
                <prop key="user">${jdbc_jcs.username}</prop>
                <prop key="password">${jdbc_jcs.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 航空所數據源-->
    <bean id="ds_hks" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/hks" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_hks.url.spider}</prop>
                <prop key="user">${jdbc_hks.username}</prop>
                <prop key="password">${jdbc_hks.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 系統所數據源-->
    <bean id="ds_xts" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/xts" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_xts.url.spider}</prop>
                <prop key="user">${jdbc_xts.username}</prop>
                <prop key="password">${jdbc_xts.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 信息工程所數據源-->
   <bean id="ds_xxgcs" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/xxgcs" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_xxgcs.url.spider}</prop>
                <prop key="user">${jdbc_xxgcs.username}</prop>
                <prop key="password">${jdbc_xxgcs.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 自動化所數據源-->
    <bean id="ds_zdhs" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/zdhs" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_zdhs.url.spider}</prop>
                <prop key="user">${jdbc_zdhs.username}</prop>
                <prop key="password">${jdbc_zdhs.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 規範所數據源-->
   <bean id="ds_gfs" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/gfs" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_gfs.url.spider}</prop>
                <prop key="user">${jdbc_gfs.username}</prop>
                <prop key="password">${jdbc_gfs.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 科信所數據源-->
    <bean id="ds_kxs" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/kxs" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_kxs.url.spider}</prop>
                <prop key="user">${jdbc_kxs.username}</prop>
                <prop key="password">${jdbc_kxs.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 翻譯隊數據源-->
   <bean id="ds_fyd" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/fyd" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_fyd.url.spider}</prop>
                <prop key="user">${jdbc_fyd.username}</prop>
                <prop key="password">${jdbc_fyd.password}</prop>
            </props>
        </property>
    </bean>
    
    <!-- 院本級數據源-->
    <bean id="ds_ybj" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/ybj" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_ybj.url.spider}</prop>
                <prop key="user">${jdbc_ybj.username}</prop>
                <prop key="password">${jdbc_ybj.password}</prop>
            </props>
        </property>
    </bean>

    <!-- 院綜合系統數據源-->
    <bean id="ds_yzh" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="mysql/yzh" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
               <prop key="URL">${jdbc_yzh.url.spider}</prop>
                <prop key="user">${jdbc_yzh.username}</prop>
                <prop key="password">${jdbc_yzh.password}</prop>
            </props>
        </property>
    </bean>
  
    <!-- 配置多數據源 MultipleDataSource-->
    <bean name="dynamicDatasource" class="com.fms.common.datasource.MultipleDataSource">
        <property name="targetDataSources">
            <map>
                <!-- key和value-ref儘可能保持一致,我在測試的時候由於名稱不一致一致報錯,
                找了很久都沒找到緣由,最後統一了名稱終於成功啓動了 -->
                <entry key="ds_zhs" value-ref="ds_zhs"/>
                <entry key="ds_jcs" value-ref="ds_jcs"/>
                 <entry key="ds_hks" value-ref="ds_hks"/>
                <entry key="ds_xts" value-ref="ds_xts"/>
                <entry key="ds_xxgcs" value-ref="ds_xxgcs"/>
                <entry key="ds_zdhs" value-ref="ds_zdhs"/>
                <entry key="ds_gfs" value-ref="ds_gfs"/>
                <entry key="ds_kxs" value-ref="ds_kxs"/>
                <entry key="ds_fyd" value-ref="ds_fyd"/>
                <entry key="ds_ybj" value-ref="ds_ybj"/>
                <entry key="ds_yzh" value-ref="ds_yzh"/>
            </map>
        </property>
        <!-- 指定一個默認的數據源,即在不須要切換數據源時,本地系統默認使用的數據源 -->
        <property name="defaultTargetDataSource" ref="ds_zhs"    />
    </bean>
    
    <!-- 下面開始配置SqlSessionFactoryBean,有多少個數據源須要支持就陪多少個 -->
    <bean id="sqlSessionFactory_zhs" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- dataSource的ref對應數據源的id -->
        <property name="dataSource" ref="ds_zhs"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_jcs" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_jcs"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_hks" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_hks"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_xts" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_xts"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_xxgcs" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_xxgcs"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_zdhs" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_zdhs"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_gfs" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_gfs"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_kxs" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_kxs"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_fyd" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_fyd"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_ybj" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_ybj"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionFactory_yzh" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="ds_yzh"/>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
                <value>classpath*:/com/fms/**/dao/*DAO.xml</value>
            </list>
        </property>
    </bean>

    <!-- 配置自定義的SqlSessionTemplate模板,注入相關配置 -->
    <bean id="sqlSessionTemplate" class="com.fms.common.datasource.CustomSqlSessionTemplate" scope="prototype">
        <!-- 構造注入參數指定本地默認數據源對應的SqlSessionFactoryBean -->
        <constructor-arg ref="sqlSessionFactory_zhs" />
        <property name="targetSqlSessionFactorys">
            <map>     
                <!-- key和上文配置的數據源的id值儘可能保持一致,我在測試的時候由於名稱不一致一致報錯,
                找了很久都沒找到緣由,最後統一了名稱終於成功啓動了 -->
                <entry value-ref="sqlSessionFactory_zhs" key="ds_zhs"/>
                <entry value-ref="sqlSessionFactory_jcs" key="ds_jcs"/>
                <entry value-ref="sqlSessionFactory_hks" key="ds_hks"/>
                <entry value-ref="sqlSessionFactory_xts" key="ds_xts"/>
                <entry value-ref="sqlSessionFactory_xxgcs" key="ds_xxgcs"/>
                <entry value-ref="sqlSessionFactory_zdhs" key="ds_zdhs"/>
                <entry value-ref="sqlSessionFactory_gfs" key="ds_gfs"/>
                <entry value-ref="sqlSessionFactory_kxs" key="ds_kxs"/>
                <entry value-ref="sqlSessionFactory_fyd" key="ds_fyd"/>
                <entry value-ref="sqlSessionFactory_ybj" key="ds_ybj"/>
                <entry value-ref="sqlSessionFactory_yzh" key="ds_yzh"/>
            </map> 
        </property>
    </bean>
    
    <!-- 配置mybatis接口掃描 -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.fms.**.dao" />
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
    </bean>
    
    <!-- jta配置,直接複用,不須要修改 -->
    <!-- jta配置開始 -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <property name="forceShutdown">
            <value>true</value>
        </property>
    </bean>
 
    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300" />
    </bean>
 
    <bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager">
            <ref bean="atomikosTransactionManager" />
        </property>
        <property name="userTransaction">
            <ref bean="atomikosUserTransaction" />
        </property>
    </bean>
    <!-- jta配置結束 -->
    
    <!-- 配置事務管理 -->
     <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />

</beans>

  三、springmvc-servlet.xml:spring-mvc

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd    
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd    
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd    
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd    
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd    
        http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.1.xsd    
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.1.xsd    
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd    
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd    
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd    
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">

    <context:component-scan base-package="com.fms;com.jmda;com.job;" >      
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />    
    </context:component-scan>
    <!-- <context:annotation-config/> -->
    
    <!-- message-converters -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/plain;charset=UTF-8</value>
                        <value>text/html;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    
    <!-- 視圖解析器 -->
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="true" />
        <property name="suffix" value=".ftl" />
        <property name="contentType" value="text/html;charset=UTF-8"></property>
        <property name="requestContextAttribute" value="request" />
        <property name="exposeSpringMacroHelpers" value="true" />
        <property name="exposeRequestAttributes" value="true" />
        <property name="exposeSessionAttributes" value="true" />
    </bean>
    
    <!-- 靜態資源 -->
    <mvc:resources mapping="/static/**" location="/static/" />
    <mvc:resources mapping="/jmda-static/**" location="/jmda-static/" />
    <mvc:resources mapping="/assets/**" location="/assets/" />
    
    <!-- 攔截器 -->
    <mvc:interceptors>
        <bean class="com.fms.common.listener.SecurityInterceptor"/>
    </mvc:interceptors>
</beans>

  四、web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
        classpath*:/spring/applicationContext*.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    </listener>
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>spring4mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:/spring/springmvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring4mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--文件下載 -->
    <servlet>
        <servlet-name>ServletDownload</servlet-name>
        <servlet-class>com.fms.business.sjtb.service.ServletDownload</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletDownload</servlet-name>
        <url-pattern>/servlet/download</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/classes/log4j.properties</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <listener>
        <listener-class>com.fms.common.listener.CommListener</listener-class>
    </listener>
    <!-- 400錯誤 -->
    <error-page>
        <error-code>400</error-code>
        <location>/error</location>
    </error-page>
    <!-- 404 頁面不存在錯誤 -->
    <error-page>
        <error-code>404</error-code>
        <location>/error</location>
    </error-page>
    <!-- 403 服務器拒絕請求 -->
    <error-page>
        <error-code>403</error-code>
        <location>/error</location>
    </error-page>
    <!-- 500 服務器內部錯誤 -->
    <error-page>
        <error-code>500</error-code>
        <location>/error</location>
    </error-page>
    <!-- 503 服務不可用 -->
    <error-page>
        <error-code>503</error-code>
        <location>/error</location>
    </error-page>
    <!-- java.lang.Exception -->
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/error</location>
    </error-page>
    <!-- java.lang.NullPointerException -->
    <error-page>
        <exception-type>java.lang.NullPointerException</exception-type>
        <location>/error</location>
    </error-page>
    <error-page>
        <exception-type>javax.servlet.ServletException</exception-type>
        <location>/error</location>
    </error-page>
    <welcome-file-list>
    <welcome-file></welcome-file>
    </welcome-file-list>
</web-app>

  五、在resources文件夾下增長jta.properties文件:

com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name = tm.out
com.atomikos.icatch.log_base_name = tmlog
com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
com.atomikos.icatch.console_log_level=WARN

 

JAVA代碼:

  1.MultipleDataSource:

package com.fms.common.datasource;

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

public class MultipleDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();

    //將數據源重置爲默認數據源
    public static void changeTodefaultDataSource() {
        dataSourceKey.remove();
    }
//    ////    public static void setDataSourceY(){
//        dataSourceKey.remove();
//        dataSourceKey.set("yzhDataSource");
//    }
   
    //將數據源設置爲配置文件中key值爲dataSource參數對應的值的數據源
    public static void setDataSource(String dataSource){
        dataSourceKey.remove();
        dataSourceKey.set(dataSource);
    }
    
    //獲取當前數據源的key值
    public static String getKey(){
        return dataSourceKey.get();
    }
    
    //重寫AbstractRoutingDataSource的方法,提供當前的數據源用於鏈接
    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceKey.get();
    }

}

  二、自定義的SqlSessionTemlate類:

  CustomSqlSessionTemplate

package com.fms.common.datasource;

import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;


public class CustomSqlSessionTemplate extends SqlSessionTemplate {
     
    private final SqlSessionFactory sqlSessionFactory;
    private final ExecutorType executorType;
    private final SqlSession sqlSessionProxy;
    private final PersistenceExceptionTranslator exceptionTranslator;
 
    private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
    private SqlSessionFactory defaultTargetSqlSessionFactory;
 
    public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
        this.targetSqlSessionFactorys = targetSqlSessionFactorys;
    }
 
    public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
        this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
    }
 
    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }
 
    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
                .getEnvironment().getDataSource(), true));
    }
 
    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
            PersistenceExceptionTranslator exceptionTranslator) {
 
        super(sqlSessionFactory, executorType, exceptionTranslator);
 
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
                SqlSessionFactory.class.getClassLoader(),
                new Class[] { SqlSession.class }, 
                new SqlSessionInterceptor());
 
        this.defaultTargetSqlSessionFactory = sqlSessionFactory;
    }
 
    @Override
    public SqlSessionFactory getSqlSessionFactory() {
 
        SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(MultipleDataSource.getKey());
        if (targetSqlSessionFactory != null) {
            return targetSqlSessionFactory;
        } else if (defaultTargetSqlSessionFactory != null) {
            return defaultTargetSqlSessionFactory;
        } else {
            Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
            Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
        }
        return this.sqlSessionFactory;
    }
 
    @Override
    public Configuration getConfiguration() {
        return this.getSqlSessionFactory().getConfiguration();
    }
 
    public ExecutorType getExecutorType() {
        return this.executorType;
    }
 
    public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
        return this.exceptionTranslator;
    }
 
    /**
     * {@inheritDoc}
     */
    public <T> T selectOne(String statement) {
        return this.sqlSessionProxy.<T> selectOne(statement);
    }
 
    /**
     * {@inheritDoc}
     */
    public <T> T selectOne(String statement, Object parameter) {
        return this.sqlSessionProxy.<T> selectOne(statement, parameter);
    }
 
    /**
     * {@inheritDoc}
     */
    public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
        return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
    }
 
    /**
     * {@inheritDoc}
     */
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
        return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
    }
 
    /**
     * {@inheritDoc}
     */
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
        return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
    }
 
    /**
     * {@inheritDoc}
     */
    public <E> List<E> selectList(String statement) {
        return this.sqlSessionProxy.<E> selectList(statement);
    }
 
    /**
     * {@inheritDoc}
     */
    public <E> List<E> selectList(String statement, Object parameter) {
        return this.sqlSessionProxy.<E> selectList(statement, parameter);
    }
 
    /**
     * {@inheritDoc}
     */
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
    }
 
    /**
     * {@inheritDoc}
     */
    public void select(String statement, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, handler);
    }
 
    /**
     * {@inheritDoc}
     */
    public void select(String statement, Object parameter, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, parameter, handler);
    }
 
    /**
     * {@inheritDoc}
     */
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
    }
 
    /**
     * {@inheritDoc}
     */
    public int insert(String statement) {
        return this.sqlSessionProxy.insert(statement);
    }
 
    /**
     * {@inheritDoc}
     */
    public int insert(String statement, Object parameter) {
        return this.sqlSessionProxy.insert(statement, parameter);
    }
 
    /**
     * {@inheritDoc}
     */
    public int update(String statement) {
        return this.sqlSessionProxy.update(statement);
    }
 
    /**
     * {@inheritDoc}
     */
    public int update(String statement, Object parameter) {
        return this.sqlSessionProxy.update(statement, parameter);
    }
 
    /**
     * {@inheritDoc}
     */
    public int delete(String statement) {
        return this.sqlSessionProxy.delete(statement);
    }
 
    /**
     * {@inheritDoc}
     */
    public int delete(String statement, Object parameter) {
        return this.sqlSessionProxy.delete(statement, parameter);
    }
 
    /**
     * {@inheritDoc}
     */
    public <T> T getMapper(Class<T> type) {
        return getConfiguration().getMapper(type, this);
    }
 
    /**
     * {@inheritDoc}
     */
    public void commit() {
        throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
    }
 
    /**
     * {@inheritDoc}
     */
    public void commit(boolean force) {
        throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
    }
 
    /**
     * {@inheritDoc}
     */
    public void rollback() {
        throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
    }
 
    /**
     * {@inheritDoc}
     */
    public void rollback(boolean force) {
        throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
    }
 
    /**
     * {@inheritDoc}
     */
    public void close() {
        throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
    }
 
    /**
     * {@inheritDoc}
     */
    public void clearCache() {
        this.sqlSessionProxy.clearCache();
    }
 
    /**
     * {@inheritDoc}
     */
    public Connection getConnection() {
        return this.sqlSessionProxy.getConnection();
    }
 
    /**
     * {@inheritDoc}
     * @since 1.0.2
     */
    public List<BatchResult> flushStatements() {
        return this.sqlSessionProxy.flushStatements();
    }
 
    /**
     * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
     * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
     * the {@code PersistenceExceptionTranslator}.
     */
    private class SqlSessionInterceptor implements InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            final SqlSession sqlSession = getSqlSession(
                    CustomSqlSessionTemplate.this.getSqlSessionFactory(),
                    CustomSqlSessionTemplate.this.executorType, 
                    CustomSqlSessionTemplate.this.exceptionTranslator);
            try {
                Object result = method.invoke(sqlSession, args);
                if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) {
                    // force commit even on non-dirty sessions because some databases require
                    // a commit/rollback before calling close()
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator
                        .translateExceptionIfPossible((PersistenceException) unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }
                throw unwrapped;
            } finally {
                closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory());
            }
        }
    }
 
}

  三、數據源切換切面ChooseDataSourceAspect:

package com.fms.common.aop;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import com.fms.common.annotation.ChooseDataSource;
import com.fms.common.datasource.MultipleDataSource;
import com.fms.common.utils.reflect.ReflectUtil;


/**
 * 類描述:完成數據源的切換,抽類切面,具體項目繼承一下,不須要重寫便可使用
 */
@Aspect
public abstract class ChooseDataSourceAspect {

    protected static final ThreadLocal<String> preDatasourceHolder = new ThreadLocal<String>();


    @Pointcut("@annotation(com.fms.common.annotation.ChooseDataSource)")
    public void methodWithChooseAnnotation() {
        System.err.println("************************************************//////////////////////////////////////////");
    }

    /**
     * 根據@ChooseDataSource的屬性值設置不一樣的dataSourceKey,以供DynamicDataSource
     */
    @Before("methodWithChooseAnnotation()")
    public void changeDataSourceBeforeMethodExecution(JoinPoint jp) {
        //拿到anotation中配置的數據源
        String resultDS = determineDatasource(jp);
        //沒有配置實用默認數據源
        if (resultDS == null) {
            MultipleDataSource.changeTodefaultDataSource();
            return;
        }
        preDatasourceHolder.set(MultipleDataSource.getKey());
        //將數據源設置到數據源持有者
        MultipleDataSource.setDataSource(resultDS);

    }

    /**
     * 若是須要修改獲取數據源的邏輯,請重寫此方法    *
     * @param jp
     * @return
     */
    @SuppressWarnings("rawtypes")
    protected String determineDatasource(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        Class targetClass = jp.getSignature().getDeclaringType();
        String dataSourceForTargetClass = resolveDataSourceFromClass(targetClass);
        String dataSourceForTargetMethod = resolveDataSourceFromMethod(targetClass, methodName);
        String resultDS = determinateDataSource(dataSourceForTargetClass, dataSourceForTargetMethod);
        return resultDS;
    }


    /**
     * 方法執行完畢之後,數據源切換回以前的數據源。
     * 好比foo()方法裏面調用bar(),可是bar()另一個數據源,
     * bar()執行時,切換到本身數據源,執行完之後,要切換到foo()所須要的數據源,以供
     * foo()繼續執行。
     */
    @After("methodWithChooseAnnotation()")
    public void restoreDataSourceAfterMethodExecution() {
        MultipleDataSource.setDataSource(preDatasourceHolder.get());
        preDatasourceHolder.remove();
    }


    /**
     * @param targetClass
     * @param methodName
     * @return
     */
    @SuppressWarnings("rawtypes")
    private String resolveDataSourceFromMethod(Class targetClass, String methodName) {

        Method m = ReflectUtil.findUniqueMethod(targetClass, methodName);
        if (m != null) {
            ChooseDataSource choDs = m.getAnnotation(ChooseDataSource.class);
            return resolveDataSourcename(choDs);
        }
        return null;
    }

    /**
     * 方法描述 : 
     * 肯定最終數據源,若是方法上設置有數據源,則以方法上的爲準,若是方法上沒有設置,則以類上的爲準,若是類上沒有設置,則使用默認數據源
     *
     * @param classDS
     * @param methodDS
     * @return
     */
    private String determinateDataSource(String classDS, String methodDS) {
//        if (null == classDS && null == methodDS) {
//            return null;
//        }
        // 二者必有一個不爲null,若是二者都爲Null,也會返回Null
        return methodDS == null ? classDS : methodDS;
    }

    /**
     * 方法描述 : 類級別的 @ChooseDataSource的解析
     *
     * @param targetClass
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private String resolveDataSourceFromClass(Class targetClass) {
        ChooseDataSource classAnnotation = (ChooseDataSource) targetClass
                .getAnnotation(ChooseDataSource.class);
        // 直接爲整個類進行設置
        return null != classAnnotation ? resolveDataSourcename(classAnnotation)
                : null;
    }

    /**
     * 方法描述 :
     * 組裝DataSource的名字
     *
     * @param ds
     * @return
     */
    private String resolveDataSourcename(ChooseDataSource ds) {
        return ds == null ? null : ds.value();
    }

}

 

  四、數據源切換註解ChooseDataSource:

package com.fms.common.annotation;

import java.lang.annotation.*;

/**
 * 註解式數據源,用來進行數據源切換
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChooseDataSource {

    String value() default "";
}

  五、使用方式:

  TestService:

package com.fms.business;

import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fms.common.dao.table.mapper.WfWorkflowPOMapper;
import com.fms.common.datasource.MultipleDataSource;
import com.fms.common.model.po.WfWorkflowPO;
import com.google.common.collect.Maps;

@Service(value="testService")
public class TestService {
    
    //注入SqlSessionTemplate,執行自定義的Mapper.xml文件
    @Autowired
    SqlSessionTemplate sqlSessionTemplate;
    String nameSpace = "com.fms.business.dao.TestServiceMapper";

    
    @Transactional
    public void test() throws Exception{
        Map<String, Object> param = Maps.newHashMap();
        param.put("id", "123");
        param.put("name", "zhangsan");
        //默認數據源ds_zhs
        sqlSessionTemplate.insert(nameSpace+".testInsert", param);
        //切換至ds_jcs
        MultipleDataSource.setDataSource("ds_jcs");
        sqlSessionTemplate.insert(nameSpace+".testInsert", param);
    }
    
    @Transactional
    public void test1() throws Exception{
        Map<String, Object> param = Maps.newHashMap();
        param.put("id", "124");
        param.put("name", "lisi");
        //默認數據源ds_zhs
        sqlSessionTemplate.insert(nameSpace+".testInsert", param);
        //切換至ds_jcs
        MultipleDataSource.setDataSource("ds_jcs");
        sqlSessionTemplate.insert(nameSpace+".testInsert", param);
        //編寫拋出異常的代碼測試事務回滾
        String str = null;
        str.trim();
    }
    
    //使用MyBatis自動生成的Mapper接口
    @Autowired
    WfWorkflowPOMapper wfWorkflowPOMapper;
    @Transactional
    public void test2() throws Exception{
        WfWorkflowPO record = new WfWorkflowPO();
        record.setWorkflowId("12345");
        record.setName("xxxx");
        wfWorkflowPOMapper.insert(record);
        //切換至ds_jcs
        MultipleDataSource.setDataSource("ds_jcs");
        wfWorkflowPOMapper.insert(record);
        //切換至ds_yzh
        MultipleDataSource.setDataSource("ds_yzh");
        wfWorkflowPOMapper.insert(record);
    }
    
}

  TestController:

package com.fms.business;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
    
    @Autowired
    TestService testService;
    
    @RequestMapping("/test")
    @ResponseBody
    public String test(){
        try {
            testService.test();
            return "ok";
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
    
    @RequestMapping("/test1")
    @ResponseBody
    public String test1(){
        try {
            testService.test1();
            return "ok";
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
    
    @RequestMapping("/test2")
    @ResponseBody
    public String test2(){
        try {
            testService.test2();
            return "ok";
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }
}Aspect

  原例中還有使用@ChooseDataSource註解切換數據源的用法,須要AspectJ編譯,搗鼓了一下沒成功,數據源可以切換,可是分佈式事務不起做用,有興趣能夠本身試試本身調試修改。

  在項目中加入一個繼承ChooseDataSourceAspect的類便可:

package com.fms.business;ChooseDataSource

import org.aspectj.lang.annotation.Aspect;

import com.fms.common.aop.ChooseDataSourceAspect;

@Component @Aspect
public class TestAspect extends ChooseDataSourceAspect { }

 

 

  參考博文:http://www.blogjava.net/zuxiong/archive/2015/09/24/427471.html

相關文章
相關標籤/搜索