使用Spring的AbstractRoutingDataSource实现Mybatis多数据源及事务

环境 :spring+mybatis,非注解配置
1.准备两个数据库

# MySQL DataSource 1
ip.zqp.1=192.168.254.5
username.wdk.1=dev
password.wdk.1=9tong
dbname.wdk.1=zqp
    
# MySQL DataSource 2
ip.zqp.2=192.168.254.54
username.wdk.2=root
password.wdk.2=root
dbname.wdk.2=zqp

2.Mybatis-Spring.xml配置文件中配置这两个数据源

<bean id="dataSourceZqp1" class="com.jolbox.bonecp.BoneCPDataSource"
    destroy-method="close">
    <!-- 数据库驱动 -->
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <!-- 相应驱动的jdbcUrl -->
    <property name="jdbcUrl"
        value="jdbc:mysql://${ip.zqp.1}:3306/${dbname.wdk.1}?user=${username.wdk.1}&amp;password=${password.wdk.1}&amp;useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;zeroDateTimeBehavior=convertToNull" />
    <!-- 数据库的用户名 -->
    <property name="username" value="${username.wdk.1}" />
    <!-- 数据库的密码 -->
    <property name="password" value="${password.wdk.1}" />
    <!-- 分区 -->
    <property name="partitionCount" value="3" />
    <!-- 每个分区的最大连接数 -->
    <property name="maxConnectionsPerPartition" value="100" />
    <!-- 设置测试connection的间隔时间,这个参数默认为240,单位:分钟,设置为0该功能失效 -->
    <property name="idleConnectionTestPeriod" value="60" />

    <!-- 设置connection的存活时间,这个参数默认为0,单位:毫秒,设置为0该功能失效 -->
    <property name="maxConnectionAge" value="3000000 " />
    <!-- 数据库连接空闲时间 -->
    <property name="idleMaxAge" value="60" />
</bean>

<bean id="dataSourceZqp2" class="com.jolbox.bonecp.BoneCPDataSource"
    destroy-method="close">
    <!-- 数据库驱动 -->
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <!-- 相应驱动的jdbcUrl -->
    <property name="jdbcUrl"
        value="jdbc:mysql://${ip.zqp.2}:3306/${dbname.wdk.2}?user=${username.wdk.2}&amp;password=${password.wdk.2}&amp;useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;zeroDateTimeBehavior=convertToNull" />
    <!-- 数据库的用户名 -->
    <property name="username" value="${username.wdk.2}" />
    <!-- 数据库的密码 -->
    <property name="password" value="${password.wdk.2}" />
    <!-- 分区 -->
    <property name="partitionCount" value="3" />
    <!-- 每个分区的最大连接数 -->
    <property name="maxConnectionsPerPartition" value="100" />
    <!-- 设置测试connection的间隔时间,这个参数默认为240,单位:分钟,设置为0该功能失效 -->
    <property name="idleConnectionTestPeriod" value="60" />

    <!-- 设置connection的存活时间,这个参数默认为0,单位:毫秒,设置为0该功能失效 -->
    <property name="maxConnectionAge" value="3000000 " />
    <!-- 数据库连接空闲时间 -->
    <property name="idleMaxAge" value="60" />
</bean>

3.使用AbstractRoutingDataSource,自定义MultipleDataSource类

/**
 * 
 */
package com.jiutong.datasource;

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

/**
 *
 * @author song.ty
 * @createTime : 2016年1月12日 上午9:52:04
 * @version : 1.0 
 * @description : 
 *
 **/
public class MultipleDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();

    public static void setDataSourceKey(String dataSource) {
        dataSourceKey.set(dataSource);
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceKey.get();
    }
}

然后在mybatis-spring.xml配置文件中加入配置

<bean id="multipleDataSource" class="com.jiutong.datasource.MultipleDataSource">
    <property name="defaultTargetDataSource" ref="dataSourceZqp1"/>
    <property name="targetDataSources">
        <map>
            <entry key="dataSourceZqp1" value-ref="dataSourceZqp1"/>
            <entry key="dataSourceZqp2" value-ref="dataSourceZqp2"/>
        </map>
    </property>
</bean>

同时sqlSessionFactory的dataSource也要改成刚刚配置的多数据源合一的新数据源multipleDataSource了

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="mapperLocations" value="classpath*:/com/jiutong/dao/*/*/*.xml"></property>
    <property name="dataSource" ref="multipleDataSource" />
    <property name="plugins">
    <!-- 分页插件    -->
    <array>
      <bean class="com.github.pagehelper.PageHelper">
        <property name="properties">
          <value>
            dialect=hsqldb
          </value>
        </property>
      </bean>
    </array>
  </property>
</bean>

4.配置aop,实现动态切换,此时要注意与事务相关的配置
首先,建切面类,在前置通知里使用MultipleDataSource.setDataSourceKey("dataSourceZqp2");将数据源切换成dataSourceZqp2

package com.jiutong.datasource;

import org.apache.commons.logging.Log;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 日志切面类
 * 
 * @author 曹胜欢
 */
public class DataSourceAspect {
     Logger logger = Logger.getLogger(DataSourceAspect.class);
    // 任何通知方法都可以将第一个参数定义为 org.aspectj.lang.JoinPoint类型
    public void before(JoinPoint call) {
        // 获取目标对象对应的类名
        String className = call.getTarget().getClass().getName();
        // 获取目标对象上正在执行的方法名
        String methodName = call.getSignature().getName();
        System.out.println("前置通知:" + className + "类的" + methodName + "方法开始了");
        //logger.debug("前置通知:" + className + "类的" + methodName + "方法开始了");
        MultipleDataSource.setDataSourceKey("dataSourceZqp2");
    }
}

然后,配置文件中

<aop:config>
    <aop:pointcut id="pc"
        expression="execution(public * com.jiutong.service.*.*.*(..))" /> <!--把事务控制在Service层 -->
    <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" order="2" />
</aop:config>


<bean id="dataSourceAspectBean" class="com.jiutong.datasource.DataSourceAspect"/>  
<aop:config>  
    <aop:aspect id="dataSourceAspect" ref="dataSourceAspectBean" order="1">  
        <aop:pointcut id="allMethod"   
            expression="execution(* com.jiutong.service.*.*.*(..))"/>   
        <aop:before method="before" pointcut-ref="allMethod" />  
    </aop:aspect>  
</aop:config>

此时要注意事务advisor与数据源切换aspect的order设置,数据源的切换要在事务前

5.至此,com.jiutong.service包下的所有service使用的都是数据源2了,当然还可以配置其他com.jiutong.service以外的其他service使用数据源1,这样就实现了动态切换。

6.以上所述的配置是在不分业务多数据源的场景下,此外还有分业务多数据源的情况,与此类似。

(全文完)

(转载本站文章请注明作者和出处 使用Spring的AbstractRoutingDataSource实现Mybatis多数据源及事务