AbstractRoutingDataSource 只支持单库事务,也就是说切换数据源要在开启事务之前执行。 spring DataSourceTransactionManager进行事务管理,开启事务,会将数据源缓存到DataSourceTransactionObject对象中进行后续的commit rollback等事务操作。

出现多数据源动态切换失败的原因是因为在事务开启后,数据源就不能再进行随意切换了,也就是说,一个事务对应一个数据源。

1.传统的Spring管理事务是放在Service业务层操作的,所以更换数据源的操作要放在这个操作之前进行。也就是切换数据源操作放在Controller层,可是这样操作会造成Controller层代码混乱的结果。

故而想到的解决方案是将事务管理在数据持久 (Dao层) 开启,切换数据源的操作放在业务层进行操作,就可在事务开启之前顺利进行数据源切换,不会再出现切换失败了。

2.如果管理事务是放在Service业务层操作的,Spring事务会在方法前获取数据连接connection,动态数据源的切面是在Dao层,但是这时还没有到DAO层进行路由选择,因此需要延迟加载数据源,需要用到LazyConnectionDataSourceProxy

<bean id="parentDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialSize" value="1"/>
<property name="minIdle" value="2"/>
<property name="validationQuery" value="SELECT 1 FROM DUAL"/>
<property name="testOnCreate" value="true"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="true"/>
</bean> <bean id="dataSource" parent="parentDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean> <bean id="dataSourceM2" parent="parentDataSource">
<property name="driverClassName" value="${jdbcM2.driverClassName}"/>
<property name="url" value="${jdbcM2.url}"/>
<property name="username" value="${jdbcM2.username}"/>
<property name="password" value="${jdbcM2.password}"/>
</bean> <bean id="dataSourceR" parent="parentDataSource">
<property name="driverClassName" value="${jdbcR.driverClassName}" />
<property name="url" value="${jdbcR.url}" />
<property name="username" value="${jdbcR.username}" />
<property name="password" value="${jdbcR.password}" />
</bean> <bean id="dynamicDataSource" class="com.cmcc.open.ss.config.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource" value-ref="dataSource"/>
<entry key="dataSourceM2" value-ref="dataSourceM2"/>
<entry key="dataSourceR" value-ref="dataSourceR"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource"/>
</bean> <!-- Spring事务会在方法前获取数据连接connection,但是这时还没有到DAO层进行路由选择,因此需要延迟加载数据源,需要用到LazyConnectionDataSourceProxy。 -->
<bean id="lazyDataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="dynamicDataSource">
</property>
</bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="lazyDataSource"/>
<property name="configLocation" value="classpath:acs_mysql.xml"></property>
</bean> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="lazyDataSource"/>
</bean> <tx:annotation-driven transaction-manager="transactionManager"/>

3.如果不用LazyConnectionDataSourceProxy,可以给切面类上加上@Order(-1),让此切面优先于事务的切面执行

import lombok.extern.slf4j.Slf4j;
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.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Slf4j
@Aspect
@Order(-1)
public class DataSourceAspect {
/**
*
*/
@Pointcut("@within(com.hy.api.aop.DataSource) || @annotation(com.hy.api.aop.DataSource)")
public void pointCut() { } /**
*
* @param dataSource
*/
@Before("pointCut() && @annotation(dataSource)")
public void doBefore(DataSource dataSource) {
String ds = dataSource.value().getValue();
log.info("====================dataSource: " + ds);
MultipleDataSource.setDataSource(ds); } /**
*
*/
@After("pointCut()")
public void doAfter() {
MultipleDataSource.clear();
}
}

https://blog.csdn.net/qq_37502106/article/details/91044952

AbstractRoutingDataSource -- Spring提供的轻量级数据源切换方式的更多相关文章

  1. spring mvc+mybatis+多数据源切换

    spring mvc+mybatis+多数据源切换,选取oracle,mysql作为例子切换数据源.oracle为默认数据源,在测试的action中,进行mysql和oracle的动态切换. web. ...

  2. dubbo服务+Spring事务+AOP动态数据源切换 出错

    1:问题描述,以及分析 项目用了spring数据源动态切换,服务用的是dubbo.在运行一段时间后程序异常,更新操作没有切换到主库上. 这个问题在先调用读操作后再调用写操作会出现. 经日志分析原因: ...

  3. Spring —— 三种配置数据源的方式:spring内置、c3p0、dbcp

    01.Spring内置数据源配置Class:DriverManagerDataSource全限定名:org.springframework.jdbc.datasource.DriverManagerD ...

  4. 【Spring】使用Spring的AbstractRoutingDataSource实现多数据源切换

    最近因为项目需要在做两个项目间数据同步的需求,具体是项目1的数据通过消息队列同步到项目2中,因为这个更新操作还涉及到更新多个库的数据,所以就需要多数据源切换的操作.下面就讲讲在Spring中如何进行数 ...

  5. 使用Spring的AbstractRoutingDataSource实现多数据源切换

    https://www.cnblogs.com/softidea/p/7127874.html?utm_source=itdadao&utm_medium=referral https://b ...

  6. 30个类手写Spring核心原理之动态数据源切换(8)

    本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...

  7. Spring中AOP方式实现多数据源切换

    作者:suroot spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要 ...

  8. spring AbstractRoutingDataSource实现动态数据源切换

    使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public ...

  9. Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

    原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...

随机推荐

  1. ubuntu 替换某一内核模块

    流程 方法一 以下配置仅执行一次,并以 linux kernel 3.13.0 为例 $ cd ~ $ apt-get source linux-source-3.13.0 $ cd linux-3. ...

  2. Flyway:Spring Boot中使用Flyway来管理数据库版本

    Flyway简介 Flyway是一个简单开源数据库版本控制器(约定大于配置),主要提供migrate.clean.info.validate.baseline.repair等命令.它支持SQL(PL/ ...

  3. PHP常见的设计模式

    设计模式六大原则 开放封闭原则:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象. 依赖倒置原则:高层模块不应该依赖低层模块,二者 ...

  4. SoapUI Pro 最新版本和最新功能

    专为整个后端的端到端测试而构建 创建全面的端到端测试,以从API定义或实时端点验证API的整个工作流程.只需单击几下即可传递响应数据并添加断言-无需编码. 综合生成或配置数据 通过简单的数据驱动测试来 ...

  5. C语言:位运算符

    异或        ^     两个二进制位相同结果为0:不相同结果为1              1^1=0    1^0=1   0^1=1  0^0=1 按位或    |      两个二进制位 ...

  6. Java之注解与反射

    Java之注解与反射 注解(Annotation)简介 注解(Annotation)是从JDK5.0引入的新技术 Annotation作用:注解(Annotation)可以被其他程序如编译器等读取 A ...

  7. 浅析vue-cli脚手架命令的执行过程

    上一篇文章,已经大致了解脚手架是什么以及脚手架是如何工作的.接下来,稍微深入一下脚手架的工作过程(以vue-cli为例).首先抛出3个问题: 1.明明全局安装的是@vue/cli,最后执行的命令却是v ...

  8. 每天五分钟Go - 数组

    //数组的声明,默认为0值 var a1 [2]int fmt.Println(a1) //数组的长度 fmt.Println(len(a1)) //遍历数组 for i, v := range a1 ...

  9. 每天五分钟Go - Map

    map的定义 var m map[type]type fmt.Println(m) 此种方法定义的m为nil //打印的结果为: map[] map的创建 1.使用make创建 var m1 = ma ...

  10. SAML 2.0 实例分析 sp向idp发送请求(3)

    user没有登陆过sp,此时sp向idp发送请求,下文是请求的xml形式 <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAM ...