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

劉之西東發表於2020-12-11

Springboot min -Solon 詳解系列文章:
Springboot mini - Solon詳解(一)- 快速入門
Springboot mini - Solon詳解(二)- Solon的核心
Springboot mini - Solon詳解(三)- Solon的web開發

在前面的篇章裡我們已經見識了 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 是否為只讀事務

六、示例

  • 父回滾,子回滾
@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("不讓你加");
    }
}
  • 父回滾,子不回滾
@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("不讓你加;但還是加了:(");
    }
}
  • 子回滾父不回滾
@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){ }
    }
}
  • 多資料來源事務示例
@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專案地址

相關文章