spring事務傳播的Propagation.REQUIRES_NEW以及NEVER MANDATORY驗證,及其失效的詭異問題

gongzz發表於2023-05-08

NEVER

不使用事務,如果當前事務存在,則丟擲異常

驗證:

@Service
public class PrService {
    @Autowired
    PrDao dao;   
    @Transactional
    public void savea() {
        dao.a();//儲存第一條資料
        saveb();
    }
    @Transactional(propagation = Propagation.NEVER)
    private void saveb() {
        dao.b();//儲存第二條資料
        int i=1/0;
    }
}

寫一個controller呼叫這個savea方法,頁面看到的是/ by zero,資料庫中兩條資料都沒有插入進去,都回滾了。照說設定了Propagation.NEVER,應該saveb方法根本執行不了,報never的錯。為什麼執行了呢?

因為是直接呼叫的saveb(),不是從代理物件上呼叫的方法,改成這樣:

@Service
public class PrService {
    @Autowired
    PrDao dao;
    
    @Autowired
    PrService prService;
    
    @Transactional
    public void savea() {
        dao.a();//儲存第一條資料
        prService.saveb();
    }

    @Transactional(propagation = Propagation.NEVER)
    private void saveb() {
        dao.b();//儲存第二條資料
        int i=1/0;
    }
}

注意這裡是  prService.saveb()和前面不同,但是居然在  dao.b(); 這一行報了一個空指標的錯誤,這就詭異了,這個dao明明在上面的savea方法裡面還有值的,你知道是為什麼嗎?

原來saveb方法的修飾符是private,導致代理物件無法繼承這個方法。改成public

public void saveb()

再次測試,看到期待的報錯了:

Existing transaction found for transaction marked with propagation 'never'

 

 

MANDATORY

當前存在事務,則加入當前事務,如果當前事務不存在,則丟擲異常。

這裡把MANDATORY放在saveb方法上,就要把savea方法上的transactional註解去掉,才能看到報錯。

@Service
public class PrService {
    @Autowired
    PrDao dao;
    
    @Autowired
    PrService prService;

    public void savea() {
        dao.a();//儲存第一條資料
        prService.saveb();
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void saveb() {
        dao.b();//儲存第二條資料
    }
}

或者直接把MANDATORY放在a方法上也會報錯:

@Service
public class PrService {
    @Transactional(propagation = Propagation.MANDATORY)
    public void savea() {
    }
}

報錯如下

No existing transaction found for transaction marked with propagation 'mandatory'

 

REQUIRES_NEW

建立一個新事務,如果存在當前事務,則掛起該事務。

可以理解為設定事務傳播型別為REQUIRES_NEW的方法,在執行時,不論當前是否存在事務,總是會新建一個事務。

現在寫一個demo驗證,savea方法呼叫saveb方法,希望saveb方法裡面異常了且事務回滾,savea方法裡面不回滾

下面驗證:

同樣為了在避免事務失效,在代理物件上呼叫方法,將被呼叫的方法放到一個單獨的類中:

@Service
public class PrService {
    @Autowired
    PrDao dao;

    @Autowired
    SaveBService saveBService;

    @Transactional
    public void savea() {
        dao.a();//插入第一條資料
        saveBService.saveb();
    }
}
@Service
public class SaveBService {
    @Autowired
    PrDao dao;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveb() {
        dao.b();//插入第二條資料
        int i = 1/0;
    }
}

這裡PrService的savea方法呼叫SaveBService的saveb方法,寫一個controller訪問savea方法,頁面看到一個異常 / by zero,再檢查資料庫,兩條資料都有沒有插入進去。這是為什麼呢?看上去第二個方法已經寫到獨立的類中了。

原來是因為saveb方法丟擲的異常savea方法沒有捕獲,又丟擲去了,當然要回滾了。寫個try catch就行:

    @Transactional
    public void savea() {
        dao.a();//插入第一條資料
        try {
            saveBService.saveb();
        }catch (Exception e) {
            System.out.println("some error");
        }
    }

再次測試,第二條資料沒有插入,第一條資料插入到資料庫了,頁面也看不到報錯了。

 

相關文章