spring + mybatis + 多数据源整合事务

一、核心思想,spring提供了一个DataSource的子类,该类支持多个数据源java

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

该类的源码以下:mysql

org.springframework.jdbc.datasource.lookup;

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

AbstractRoutingDataSource AbstractDataSource InitializingBean {

    // 注意这里,这里使用Map存放多个数据源
    Map<Object, Object> targetDataSources;
 
    ... // 篇幅有限,省略其余代码,有兴趣的同窗能够自行研究,仍是支持蛮多功能的,好比JNDI等数据源方式     

    // 注意这个暴露的方法,spring在从数据源中获取数据库链接时,经过这个方法肯定数据源
    DataSource determineTargetDataSource() {
        Assert.notNull(.resolvedDataSources, );
        Object lookupKey = .determineCurrentLookupKey();
        DataSource dataSource = (DataSource).resolvedDataSources.get(lookupKey);
        (dataSource == && (.lenientFallback || lookupKey == )) {
            dataSource = .resolvedDefaultDataSource;
        }

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

    // 对外暴露的决定数据源的方法,经过外部设置数据源的key值,
    // spring自行从已存储的数据源中查找指定数据源
    Object determineCurrentLookupKey();
}

经过分析源码,能够知道咱们只须要实现如何动态设置切换数据源的方式便可,能够考虑使用注解的方式在指定的位置添加数据源注解,利用AOP动态指定数据源。程序员

自定义的数据源以下:spring

com.yao.yz.yaowangdrug.dataSource;

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

DynamicDataSource AbstractRoutingDataSource{

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


二、自定义注解,本章节不作过多解释,对于注解有兴趣的同窗请自行研究,废话一句:程序员以实际应用为主,研究行的内容请自行学习sql

com.yao.yz.yaowangdrug.dataSource;

java.lang.annotation.*;

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


三、定义一个数据源切换功能组件,其中数据源存放的方式采用ThreadLocal的形式,我是单独把存放作成一个额外的组件,这个是能够自行决定的,代码以下:数据库

com.yao.yz.yaowangdrug.dataSource;

DataSourceHolder {

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

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

    Object getDataSourceKey() {
       .get();
    }

}
com.yao.yz.yaowangdrug.dataSource;

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

java.lang.reflect.Method;

DynamicAspect {

    Logger = Logger.(DynamicAspect.);

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

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

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

}


四、在spring配置文件添加多个数据源,并将多个数据源统一归入自定义数据源的管理,具体的配置信息以下:express

<!--装载配置文件,将数据源的配置作成配置文件,方便管理,有兴趣的同窗自行参考-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="Config.properties"/>
</bean>

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

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

<!--自定义数据源,将全部的数据源归入自定义数据源管理-->
<bean id="dataSource" class="com.yao.yz.yaowangdrug.dataSource.DynamicDataSource">
<property name="targetDataSources">
<map>
         <!-- 对应spring提供的AbstractRoutingDataSource的Map -->

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


五、配置数据源切换的AOP,代码以下:apache

<!--动态决定数据源-->
<bean id="dataSourceSwitchAspect" class="com.yao.yz.yaowangdrug.dataSource.DynamicAspect"/>
<aop:config>
<aop:aspect id="dynamicAspect" ref="dataSourceSwitchAspect">
<!--数据源切换能够控制为Dao或者service层,请根据实际业务须要自行决定-->
     <aop:pointcut id="dynamicPointCut" expression="execution(* com.xx.xx.xx.dao.*.*(..))"/>
<aop:before method="switchDataSource" pointcut-ref="dynamicPointCut"/>
</aop:aspect>
</aop:config>


六、使用spring整合mybatis,和通常的整合却别在于使用的数据源为自定义数据源,代码以下:mybatis

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

注意:目前精力有限,此处为何可使用自定义多个数据源的底层原理还没来得及看,有兴趣的同窗请自行研究,若是好心的话能够简单的告知我,谢谢你了~~~app


七、将自定义数据源归入spring的事务管理器管理,配置代码以下:

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


Model类:

com.yao.yz.yaowangdrug.model;

DBModel {

    String ;

    String ;

    String getValue() {
        ;
    }

    setValue(String value) {
        .= value;
    }

    String getKey() {
        ;
    }

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

Dao接口:

com.yao.yz.yaowangdrug.dao;

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

DBDao {

    ()
    insert(DBModel dbModel) Exception;

}

Service接口:

com.yao.yz.yaowangdrug.service;

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

DBService {

    doInsert() Exception;

    doUpdate() Exception;

    testSeperateDB() Exception;

}


关于事务的控制,有一下几点说明:

一、采用申明或者注解实现事务控制时时,由于开启了事务控制,因此若是是两个不一样的数据源Dao,根据spring的事务传播特性,第二个事务开启将使用已有的事务(即将采用第一个数据源的数据库链接)进行事务操做,因此此时事务控制是失效的(即便切面执行了数据源切换)。结论就是跨数据库的事务是没法经过spring的数据库控制实现的!!!请切记。


二、同一个数据源的事务控制和普通的数据源控制是一致的,没有什么区别。

以上代码都是通过测试经过,能够实现跨库的数据方式,主要的应用场景是mysql的数据库读写分离。若是不正确的地方请告知,谢谢!

相关文章
相关标签/搜索