1. 程式人生 > >Springboot mini - Solon詳解(四)- Solon的事務傳播機制

Springboot mini - Solon詳解(四)- Solon的事務傳播機制

> 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