事务导致多数据源AbstractRoutingDataSource切换数据源失败问题解决

多数据源切换失败问题解决


@Transactional注解(事务)会导致多数据源切换失败!
在一个方法中,有多个SQL语句执行,我们习惯性的会加上@Transactional注解,但是!这只局限于这多个SQL语句(不论增删改查),都只访问一个库!(大坑!)
例如:

@Transactional
public void test(){
   mysqlMapperOne.insert(....);
   
   //此处为切换数据源的操作。。。。省略
   
   mysqlMapperTwo.insert(.....);
}

这个会导致mysqlMapperTwo的SQL语句被映射到第一个SQL语句的库中。导致报错,在第一个数据源中找不到第二个数据源的表。。。。这个问题我一直琢磨了大半天。。。
原因:
在org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin 这个类的源代码中找到了答案:

 @Override
 2     protected void doBegin(Object transaction, TransactionDefinition definition) {
 3         DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
 4         Connection con = null;
 5 
 6         try {
 7             if (txObject.getConnectionHolder() == null ||
 8                     txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
 9                 Connection newCon = this.dataSource.getConnection();
10                 if (logger.isDebugEnabled()) {
11                     logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
12                 }
13                 txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
14             }
15 
16             txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
17             con = txObject.getConnectionHolder().getConnection();
18 
19             Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
20             txObject.setPreviousIsolationLevel(previousIsolationLevel);
21 
22             // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
23             // so we don't want to do it unnecessarily (for example if we've explicitly
24             // configured the connection pool to set it already).
25             if (con.getAutoCommit()) {
26                 txObject.setMustRestoreAutoCommit(true);
27                 if (logger.isDebugEnabled()) {
28                     logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
29                 }
30                 con.setAutoCommit(false);
31             }
32 
33             prepareTransactionalConnection(con, definition);
34             txObject.getConnectionHolder().setTransactionActive(true);
35 
36             int timeout = determineTimeout(definition);
37             if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
38                 txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
39             }
40 
41             // Bind the connection holder to the thread.
42             if (txObject.isNewConnectionHolder()) {
43                 TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
44             }
45         }
46 
47         catch (Throwable ex) {
48             if (txObject.isNewConnectionHolder()) {
49                 DataSourceUtils.releaseConnection(con, this.dataSource);
50                 txObject.setConnectionHolder(null, false);
51             }
52             throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
53         }
54     }

第7-16行,在开始一个事务前,如果当前上下文的连接对象为空,获取一个连接对象,然后保存起来,下次doBegin再调用时,就直接用这个连接了,根本不做任何切换(类似于缓存命中!),说白了,就是 “他” 发现你之前调用过连接,他就不管另一个了

在方法被调用前,已经和另一个数据库发生了关系,所以他就直接从这个里面拿了,所以第二个他就无视了。

解决办法:把事务删了!!或者,先切到主库上来,这样后面再调用有事务的方法时,就仍然保持在主库的连接上。

原文链接:https://yq.aliyun.com/articles/250654
感谢作者:杨俊明

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Spring Cloud中实现多数据源切换可以通过以下步骤来完成: 1. 配置多个数据源:在application.properties或application.yml文件中,配置多个数据源的连接信息,每个数据源都有唯一的标识符。 2. 创建数据源对象:通过DataSourceBuilder类创建多个数据源对象,并将其绑定到对应的配置信息。 3. 创建多个JdbcTemplate对象:通过每个数据源对象创建对应的JdbcTemplate对象,用于执行数据库操作。 4. 创建动态数据源:通过AbstractRoutingDataSource类创建一个动态数据源,重写determineCurrentLookupKey()方法,根据需要动态切换数据源。 5. 配置事务管理器:为每个数据源配置对应的事务管理器,以便在进行事务操作时正确切换数据源。 6. 配置AOP切面:使用@Aspect注解定义一个切面类,在该类中使用@Around注解拦截需要切换数据源的方法,在方法执行前根据条件动态切换数据源。 7. 配置数据源切换规则:定义一个枚举类或常量类,列举出可用的数据源标识符,根据具体业务需要选择合适的数据源。 8. 测试:在需要切换数据源的地方调用相应的方法,并验证是否成功切换到了指定的数据源问题产生的原因: 在微服务架构中,可能会存在多个数据库实例,每个实例存储不同的业务数据。为了能够根据具体的业务需求切换到对应的数据源,需要实现多数据源切换。 相关问题: 1. 如何配置多个数据源? 2. 如何创建动态数据源,并在运行时动态切换数据源? 3. 如何配置事务管理器以支持多数据源切换? 4. 如何通过AOP切面实现方法级别的数据源切换? 5. 数据源切换是否会影响事务操作? 6. 在多数据源切换的场景下,如何处理事务一致性问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值