码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-09-20 22:32:38      阅读:466      评论:0      收藏:0      [点我收藏+]

标签:

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

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

该类的源码如下:

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动态指定数据源。

自定义的数据源如下:

com.yao.yz.yaowangdrug.dataSource;

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

DynamicDataSource AbstractRoutingDataSource{

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


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

com.yao.yz.yaowangdrug.dataSource;

java.lang.annotation.*;

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


3、定义一个数据源切换功能组件,其中数据源存放的方式采用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.());
    }

}


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

<!--装载配置文件,将数据源的配置做成配置文件,方便管理,有兴趣的同学自行参考-->
<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>


5、配置数据源切换的AOP,代码如下:

<!--动态决定数据源-->
<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>


6、使用spring整合mybatis,和一般的整合却别在于使用的数据源为自定义数据源,代码如下:

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

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


7、将自定义数据源纳入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;

}


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

1、采用申明或者注解实现事务控制时时,因为开启了事务控制,所以如果是两个不同的数据源Dao,根据spring的事务传播特性,第二个事务开启将使用已有的事务(即将采用第一个数据源的数据库连接)进行事务操作,所以此时事务控制是失效的(即使切面执行了数据源切换)。结论就是跨数据库的事务是无法通过spring的数据库控制实现的!!!请切记。


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

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




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

标签:

原文地址:http://my.oschina.net/u/1471781/blog/508906

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!