1. 程式人生 > 實用技巧 >Solon詳解(四)- Solon的事務傳播機制

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

Solon詳解系列文章:
Solon詳解(一)- 快速入門
Solon詳解(二)- Solon的核心
Solon詳解(三)- Solon的web開發
Solon詳解(四)- Solon的事務傳播機制

在前面的篇章裡我們已經見識了 Solon 對事務的控制,及其優雅曼妙的形態。該篇將對事務的傳播機制做講解。出於對使用者的學習成本考慮,Solon 借簽了Spring 的事務傳播策略;並友好的支援多資料來源事務。

一、為什麼要有傳播機制?

Solon 的事務是基於 aop 實現的,用者不用關心事務的開始、提交、回滾,只需要在方法上加 @XTran 註解。因為看不到內部情況,所以也會有些疑問:

  • 場景一:方法A呼叫了方法B,但兩個方法都有事務,這個時候如果方法B異常:
    • 是讓方法B回滾,還是兩個一起回滾?
  • 場景二:方法A呼叫了方法B,但是隻有方法A加了事務:
    • 是否讓方法B也加入方法A的事務?
    • 如果方法B異常,是否回滾方法A?
  • 場景三:方法A呼叫了方法B,兩者都有事務,方法B已經正常執行完:
    • 但方法A異常,是否需要回滾方法B的資料?

這個時候事務的傳播機制和策略就派上用場了。

二、傳播機制生效條件與特點!

基於 aop 來代理事務控制的方案 ,大都是針對於介面或類的之間呼叫才起效的;所以在同一個類中兩個方法之間的呼叫,傳播機制是無效的。瞭解這一點很重要,不然容易出烏龍事件。

  • 特點1:Solon 的事務傳播策略與Spring差不多(出於對新使用者的學習成本考慮)
  • 特點2:Solon 可方便的支援多資料來源事務
  • 特點3:Solon 可方便支援分庫框架或中介軟體的事務

三、傳播機制的策略

下面的型別都是針對於被呼叫方法來說的,理解起來要想象成兩個 service 方法的呼叫才可以。

傳番策略 說明
@XTran(group=true) 如果當前沒有事務組,則新建一個事務組;可用於管理多資料來源事務,但不會建立連結
@XTran(policy=TranPolicy.required) 如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。也是預設。
@XTran(policy=TranPolicy.requires_new) 新建事務,如果當前存在事務,把當前事務掛起。
@XTran(policy=TranPolicy.nested) 如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行與required類似的操作。
@XTran(policy=TranPolicy.mandatory) 使用當前的事務,如果當前沒有事務,就丟擲異常。
@XTran(policy=TranPolicy.supports) 支援當前事務,如果當前沒有事務,就以非事務方式執行。
@XTran(policy=TranPolicy.not_supported) 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
@XTran(policy=TranPolicy.never) 以非事務方式執行,如果當前存在事務,則丟擲異常。
  • 補充1:如果當前為事務組,required 和 nested 策略的事務,會自動加入事務組
  • 補充2:group=true 有時候可當 required 用
  • 補充3:一般最常用的是 @XTran(group = true) 和 @Tran 的組合使用

四、XTran 屬性說明

屬性 說明
value 資料來源標識
policy 事務策略
group 是否為事務組;用於管理下屬的子事務

五、示例

  • 父回滾,子回滾
@XService
public class UserService{
    @XTran
    public void addUser(UserModel user){
        //....
    }
}

@XController
public class DemoController{
    @XInject
    UserService userService; 
    
    //父回滾,子回滾
    //
    @XTran(group = true) 
    @XMapping("/user/add")
    pubblic void addUser(UserModel user){
        userService.addUser(user); 
        throw new RuntimeException("不讓你加");
    }
    
    //父回滾,子回滾
    //
    @XTran
    @XMapping("/user/add2")
    pubblic void addUser2(UserModel user){
        userService.addUser(user); 
        throw new RuntimeException("不讓你加");
    }
}
  • 父回滾,子不回滾
@XService
public class UserService{
    @XTran(policy = TranPolicy.requires_new)
    public void addUser(UserModel user){
        //....
    }
}

@XController
public class DemoController{
    @XInject
    UserService userService; 
    
    //父回滾,子不回滾
    //
    @XTran(group = true) 
    @XMapping("/user/add")
    pubblic void addUser(UserModel user){
        userService.addUser(user); 
        throw new RuntimeException("不讓你加;但還是加了:(");
    }
    
    //父回滾,子不回滾
    //
    @XTran
    @XMapping("/user/add2")
    pubblic void addUser2(UserModel user){
        userService.addUser(user); 
        throw new RuntimeException("不讓你加;但還是加了:(");
    }
}
  • 子回滾父不回滾
@XService
public class UserService{
    @XTran(policy = TranPolicy.nested)
    public void addUser(UserModel user){
        //....
        throw new RuntimeException("不讓你加");
    }
}

@XController
public class DemoController{
    @XInject
    UserService userService; 
    
    //子回滾父不回滾
    //
    @XTran(group = true) 
    @XMapping("/user/add")
    pubblic void addUser(UserModel user){
        try{
            userService.addUser(user); 
        }catch(ex){ }
    }
    
    //子回滾父不回滾
    //
    @XTran
    @XMapping("/user/add2")
    pubblic void addUser2(UserModel user){
        try{
            userService.addUser(user); 
        }catch(ex){ }
    }
}
  • 多資料來源事務示例
@XService
public class UserService{
    @XTran("db1")
    public void addUser(UserModel user){
        //....
    }
}

@XService
public class AccountService{
    @XTran("db2")
    public void addAccount(UserModel user){
        //....
    }
}

@XController
public class DemoController{
    @XInject
    AccountService accountService; 
    
    @XInject
    UserService userService; 
    
    @XTran(group = true) 
    @XMapping("/user/add")
    pubblic void addUser(UserModel user){
        userService.addUser(user);     //會執行db1事務
        
        accountService.addAccount(user);    //會執行db2事務
    }
}