# 多数据源引发transactional事务回滚失效

**Table of Contents** *generated with* [*DocToc*](https://github.com/thlorenz/doctoc)

* [现象](#现象)
* [@Transactional注解原理](#transactional注解原理)
  * [快速上手](#快速上手)
  * [debug分析](#debug分析)
* [简单总结](#简单总结)
  * [实验代码](#实验代码)

### 现象

![img.png](https://3362032755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5Adst29hxg4PwcUGbV%2Fuploads%2Fgit-blob-1ef5e8c7d0edd6a2f5184780c7f8cce5f66df242%2Ftransactional%E5%A4%B1%E6%95%88.png?alt=media)

如图， jdbcTemplate1表示数据源1，jdbcTemplate2表示数据源2

addStudent该方法执行后，数据源1也就是第一行的数据不会被插入

数据源2执行的插入会插入

并不符合之前对transactional注解的认知，transactional底层应该是通过动态代理对添加了该注解的方法添加环绕切面

伪代码应该是

```java
// 关闭自动提交事务
// 开启事务
try {
    // 执行方法
} catch (Throwable t) {
    // 回滚事务 
}
// 提交事务
```

按照上述的流程，理论上数据源1和数据源2插入的内容都应该被回滚，所以来了解下transactional的实现流程

### @Transactional注解原理

#### 快速上手

一篇不错的文章 <https://blog.csdn.net/minghao0508/article/details/125834496>

#### debug分析

核心代码主要在 `TransactionAspectSupport#invokeWithinTransaction` 中

![img.png](https://3362032755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5Adst29hxg4PwcUGbV%2Fuploads%2Fgit-blob-eefa8fd92c17f5ab464cb6044ba922f543ab851c%2Ftransactional%E6%BA%90%E7%A0%81.png?alt=media)

大概框架和预想中的差不多

1. 获取事务管理器

   这里是最重要的，他会根据transactional注解上的一些属性（如qualifier等）返回对应事务管理器

   如果没有指定，会返回@Primary注解对应的DataSource的事务管理器，而并不是根据哪个数据源先在方法执行就取谁，

   因为在这个层面感知不到方法里面的逻辑

   ```java
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   ```
2. 创建事务/执行方法逻辑

   ```java
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
   ```
3. 执行方法逻辑/异常回滚/清理事务上下文信息

   ```java
   Object retVal;
   try {
       // This is an around advice: Invoke the next interceptor in the chain.
       // This will normally result in a target object being invoked.
       retVal = invocation.proceedWithInvocation();
   }
   catch (Throwable ex) {
       // target invocation exception
       completeTransactionAfterThrowing(txInfo, ex);
       throw ex;
   }
   finally {
       cleanupTransactionInfo(txInfo);
   }
   ```
4. 正常提交

   ```java
   commitTransactionAfterReturning(txInfo);
   ```

### 简单总结

结论：跨数据源用transactional注解无法完全回滚，只能回滚指定数据源的数据，如果需要回滚所有数据源的数据，需要 自行定义注解（支持传入多个transactionManager）完成切面逻辑，针对多个transactionManager分别开启事务/提交事务/回滚事务

#### 实验代码

<https://github.com/icankeep/transactional-lab>

#### 自定义多数据源回滚注解

<https://blog.csdn.net/u014644574/article/details/128857774>
