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


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的方法。

更多原創閱讀:點擊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM