關於Mysql事務,你必須知道的幾個知識點!

大盛玩java發表於2021-06-28

Transaction事務

上期我們講到了jpa的常用操作,查詢、更新、刪除等,但是如果在運算元據庫事務時發生異常,資料會回滾嗎?下面我們來看個例子

UserController新增如下程式碼:

@GetMapping("save1")

public String save1(){
    User user = new User();
    user.setDptId(1L);
    user.setName("a");
    user.setAge(18L);
    user.setEmail("a@a.com");
    user.setHeadImg("headImg1");

    this.userJpa.save(user);
    //模擬發生了異常
    System.out.println(1/0);
    return "ok";
}

使用postman請求

localhost:8080/user/save1

執行之後可以看到java後臺報錯了,postman前臺也報出來錯誤,但是資料卻儲存進去了,資料新增了一條記錄

關於Mysql事務,你必須知道的幾個知識點!

說明即使發生了異常,資料還是會儲存進去資料庫,那應該怎麼辦呢?試試在save1方法上加一個@Transactional的註解。

我們再執行一次。發現錯誤也報出來了,但是資料庫並沒有將新資料插入進去,最新的還是上一次的id為7的記錄,那麼 Transactional註解是幹嘛的呢?

@Transactional是宣告式事務管理程式設計中使用的註解

  1. 該註解是新增在實現類或者介面實現方法上,而不能放在介面
  2. 需要注意的是這個註解只對public方法生效

如下是該註解的屬性,我們需要關注重點關注的是rollback-for和propagation兩個屬性。

屬性名說明
name 當在配置檔案中有多個 TransactionManager , 可以用該屬性指定選擇哪個事務管理器。
propagation 事務的傳播行為,預設值為 REQUIRED。
isolation 事務的隔離度,預設值採用 DEFAULT。
timeout 事務的超時時間,預設值為-1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。
read-only 指定事務是否為只讀事務,預設值為 false;為了忽略那些不需要事務的方法,比如讀取資料,可以設定 read-only 為 true。
rollback-for 用於指定能夠觸發事務回滾的異常型別,如果有多個異常型別需要指定,各型別之間可以通過逗號分隔。
no-rollback- for 丟擲 no-rollback-for 指定的異常型別,不回滾事務。

rollback-for:只有執行的異常才回滾。但是我們剛剛的程式並沒有指定異常,那是預設的是遇到什麼樣的異常會回滾呢?

  1. 將UserController中的程式碼稍作修改,手動throw new Exception("test"),再執行下postman,發現事務提交了,並沒有回滾。
  2. 接著我們將註解修改為@Transactional(rollbackFor = Exception.class),再執行postman,事務卻回滾了,並沒有提交,什麼原因?
  3. spring的@Transactional註解可以很方便的開啟事務,但是預設只在遇到執行時異常Error時才會回滾,非執行時異常不回滾,即Exception的子類中,除了RuntimeException及其子類,其他的類預設不回滾。
  4. 而rollbackFor屬性可以解決這個問題,rollbackFor = Exception.class表示Exception及其子類的異常都會觸發回滾,同時不影響Error的回滾。

propagation:這個用得最廣的需求就是業務出錯了,但是日誌必須提交到資料庫。怎麼處理?來看下面的程式碼。

新增LogService類

@Service

public class LogService {
    @Resource
    private UserJpa userJpa;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(){
        User user = new User();
        user.setDptId(1L);
        user.setName("log");
        user.setAge(18L);
        user.setEmail("log@log.com");
        user.setHeadImg("log");

        this.userJpa.save(user);
        System.out.println("log");
    }
}

新增UserService類:

@Service

public class UserService {
    @Resource
    private UserJpa userJpa;
    @Resource
    private LogService logService;

    @Transactional(rollbackFor = Exception.class)
    public void saveBiz() throws Exception {
        System.out.println("save2");
        User user = new User();
        user.setDptId(1L);
        user.setName("biz");
        user.setAge(18L);
        user.setEmail("biz@biz.com");
        user.setHeadImg("biz");

        this.userJpa.save(user);

        //模擬儲存日誌
        this.logService.saveLog();
        //模擬發生了異常
        throw new Exception("test1");
    }
}

UserController新增程式碼

@GetMapping("save2")

public String save2() throws Exception {
    //模擬業務操作
    this.userService.saveBiz();
    return "ok";
}

postman執行下,是不是隻有log的那條記錄插入進去了?biz的沒有插入進去。

注意:同一個業務類裡面 , 即使宣告為 Propagation.REQUIRES_NEW也不會新啟一個事務。必須呼叫另一個類的Propagation.REQUIRES_NEW方法才行。所以樣例中是使用UserService裡面呼叫另一個類LogService中的saveLog的方法。

更多原創閱讀:點選

相關文章