• spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理


    背景:

      1、系统采用SSM架构、需要在10多个MYSQL数据库之间进行切换并对数据进行操作,上篇博文《springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法》

     

      2、第一步先通过AbstractRoutingDataSource实现了多数据源的灵活切换,但是后来发现事务不起作用;

      3、发现问题是因为重复扫描service包导致第二次扫入容器的BEAN没有纳入事务管理,因此在springMVC的配置文件中排除了对Service注解的扫描,防止重复扫描,事务生效了,测试又发现数据源不能成功切换了;

      4、查阅资料发现是由于spring默认提供的事务管理DataSourceTransactionManager只能管理一个数据源的事务,因此考虑使用atomikos+JTA进行分布式事务管理

     

    配置文件:

      1、jdbc.properties

      配置你需要连接的数据库资源

    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

      2、applicationContext-common.xml:

    <?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>

      3、springmvc-servlet.xml:

    <?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>

      4、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>

      5、在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();
        }
    
    }

      2、自定义的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());
                }
            }
        }
     
    }

      3、数据源切换切面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();
        }
    
    }

      4、数据源切换注解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 "";
    }

      5、使用方式:

      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

  • 相关阅读:
    Factory——工厂方法
    Subsets II
    【算法设计】快速计算积分图
    Win8驱动的兼容性问题
    Matplotlib快速上手
    Pandas —— (8)数据读取
    Pandas —— (7)数据分组
    Pandas —— (6)多个DataFrame的合并、连接、去重、替换
    Pandas —— (4)常用数学、统计方法
    Pandas —— (3)数据查看、转置、添加、修改、删除、运算(对齐)、排序
  • 原文地址:https://www.cnblogs.com/LiQ0116/p/7027049.html
Copyright © 2020-2023  润新知