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

最新文章

  1. 使用VBScript实现设置系统环境变量的小程序
  2. C# Enum Name String Description之间的相互转换
  3. js中Unicode转义序列
  4. 并列统计CASE WHEN
  5. Linux初记
  6. 炉石传说 C# 开发笔记(BS上线尝试)
  7. shell脚本实现随机筛选
  8. Codeforces Round #286 Div.1 A Mr. Kitayuta, the Treasure Hunter --DP
  9. SWIFT 闭包的简单使用
  10. 百度分享如何自定义分享url和内容?
  11. Native App执行JS
  12. 在 Android 的 IM 应用中使用 asmack 库实现用户头像的传输(基于VCard协议)
  13. 编译u-boot命令和u-boot常用命令
  14. bower的权限问题
  15. libeXosip2(1-3) -- How-To send or update registrations.
  16. 第三十六节,os系统级别操作模块
  17. mysql中SQL执行过程详解与用于预处理语句的SQL语法
  18. 美国站群服务器有利于SEO优化
  19. ajax实现用户名校验的传统和jquery的$.post方式
  20. js中子窗口调用父窗口中的变量、函数

热门文章

  1. Redis 底层数据结构之字典
  2. 触宝科技基于Apache Hudi的流批一体架构实践
  3. linux安装subversion
  4. PHP中“简单工厂模式”实例讲解(转)
  5. Jenkins+Sonar 项目构建前代码审查
  6. ESP32 ADF windows开发环境搭建 适配ADF到ESP32A1S(转)
  7. 「CF521D」 Shop
  8. Java程序设计(2021春)——第三章类的重用笔记与思考
  9. python得到当前版本号
  10. 标准化R包开发流程