Springboot mini - Solon詳解(四)- Solon的事務傳播機制
阿新 • • 發佈:2020-12-11
> Springboot min -Solon 詳解系列文章:
> [Springboot mini - Solon詳解(一)- 快速入門](https://www.cnblogs.com/noear/p/14115763.html)
> [Springboot mini - Solon詳解(二)- Solon的核心](https://www.cnblogs.com/noear/p/14115817.html)
> [Springboot mini - Solon詳解(三)- Solon的web開發](https://www.cnblogs.com/noear/p/14115846.html)
> 在前面的篇章裡我們已經見識了 Springboot mini - Solon 對事務的控制,及其優雅曼妙的身姿。該篇將對事務及其處理策略進行詳解。出於對使用者的學習成本考慮,Solon 借簽了Spring 的事務傳播策略;所以體驗上幾乎一樣。
### 一、為什麼要有傳播機制?
Solon 對事務的控制,是使用 aop 切面實現的,所以不用關心事務的開始,提交 ,回滾,只需要在方法上加 `@Tran` 註解即可。
因為這些都是暗的,看不見的,所以也容易產生一些疑惑:
* 場景一:classA 方法呼叫了 classB 方法,但兩個方法都有事務
```
如果 classB 方法異常,是讓 classB 方法提交,還是兩個一起回滾?
```
* 場景二:classA 方法呼叫了 classB 方法,但是隻有 classA 方法加了事務
```
是否把 classB 也加入 classA 的事務,如果 classB 異常,是否回滾 classA?
```
* 場景三:classA 方法呼叫了 classB 方法,兩者都有事務,classB 已經正常執行完,但 classA 異常
```
是否需要回滾 classB 的資料?
```
這個時候,傳說中的事務傳播機制和策略就派上用場了
### 二、傳播機制生效條件
所有用 aop 實現的事務控制方案 ,都是針對於介面或類的。所以在同一個類中兩個方法的呼叫,傳播機制是不生效的。
### 三、傳播機制的策略
下面的型別都是針對於被呼叫方法來說的,理解起來要想象成兩個 class 方法的呼叫才可以。
| 傳番策略 | 說明 |
| -------- | -------- |
| TranPolicy.required | 支援當前事務,如果沒有則建立一個新的。這是最常見的選擇。也是預設。 |
| TranPolicy.requires_new | 新建事務,如果當前存在事務,把當前事務掛起。 |
| TranPolicy.nested | 如果當前有事務,則在當前事務內部巢狀一個事務;否則新建事務。 |
| TranPolicy.mandatory | 支援當前事務,如果沒有事務則報錯。 |
| TranPolicy.supports | 支援當前事務,如果沒有則不使用事務。 |
| TranPolicy.not_supported | 以無事務的方式執行,如果當前有事務則將其掛起。 |
| TranPolicy.never | 以無事務的方式執行,如果當前有事務則報錯。 |
### 四、事務的隔離級別
| 屬性 | 說明 |
| -------- | -------- |
| unspecified | 預設(JDBC預設) |
| read_uncommitted | 髒讀:其它事務,可讀取未提交資料 |
| read_committed | 只讀取提交資料:其它事務,只能讀取已提交資料 |
| repeatable_read | 可重複讀:保證在同一個事務中多次讀取同樣資料的結果是一樣的 |
| serializable | 可序列化讀:要求事務序列化執行,事務只能一個接著一個執行,不能併發執行 |
### 五、@Tran 屬性說明
| 屬性 | 說明 |
| -------- | -------- |
| policy | 事務傳導策略 |
| isolation | 事務隔離等級 |
| readOnly | 是否為只讀事務 |
### 六、示例
* 父回滾,子回滾
```java
@Service
public class UserService{
@Tran
public void addUser(UserModel user){
//....
}
}
@Controller
public class DemoController{
@Inject
UserService userService;
//父回滾,子回滾
//
@Tran
@Mapping("/user/add2")
pubblic void addUser2(UserModel user){
userService.addUser(user);
throw new RuntimeException("不讓你加");
}
}
```
* 父回滾,子不回滾
```java
@Service
public class UserService{
@Tran(policy = TranPolicy.requires_new)
public void addUser(UserModel user){
//....
}
}
@Controller
public class DemoController{
@Inject
UserService userService;
//父回滾,子不回滾
//
@Tran
@Mapping("/user/add2")
pubblic void addUser2(UserModel user){
userService.addUser(user);
throw new RuntimeException("不讓你加;但還是加了:(");
}
}
```
* 子回滾父不回滾
```java
@Service
public class UserService{
@Tran(policy = TranPolicy.nested)
public void addUser(UserModel user){
//....
throw new RuntimeException("不讓你加");
}
}
@Controller
public class DemoController{
@Inject
UserService userService;
//子回滾父不回滾
//
@Tran
@Mapping("/user/add2")
pubblic void addUser2(UserModel user){
try{
userService.addUser(user);
}catch(ex){ }
}
}
```
* 多資料來源事務示例
```java
@Service
public class UserService{
@Db("db1")
UserMapper userDao;
@Tran
public void addUser(UserModel user){
userDao.insert(user);
}
}
@Service
public class AccountService{
@Db("db2")
AccountMappeer accountDao;
@Tran
public void addAccount(UserModel user){
accountDao.insert(user);
}
}
@Controller
public class DemoController{
@Inject
AccountService accountService;
@Inject
UserService userService;
@Tran
@Mapping("/user/add")
public void addUser(UserModel user){
userService.addUser(user); //會執行db1事務
accountService.addAccount(user); //會執行db2事務
}
}
```
### 附:Solon專案地址
* gitee: [https://gitee.com/noear/solon](https://gitee.com/noear/solon)
* github: [https://github.com/noear/solon](https://github.com/noear