手把手帶你實戰下Spring的七種事務傳播行為

目錄

本文介紹Spring的七種事務傳播行為並通過代碼演示下。

一、什麼是事務傳播行為?

事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何運行。

例如:methodA方法調用methodB方法時,methodB是繼續在調用者methodA的事務中運行呢,還是為自己開啟一個新事務運行,這就是由methodB的事務傳播行為決定的。

二、事務的7種傳播行為

Spring在TransactionDefinition接口中規定了7種類型的事務傳播行為。事務傳播行為是Spring框架獨有的事務增強特性。這是Spring為我們提供的強大的工具箱,使用事務傳播行為可以為我們的開發工作提供許多便利。

7種事務傳播行為如下:

1.PROPAGATION_REQUIRED

如果當前沒有事務,就創建一個新事務,如果當前存在事務,就加入該事務,這是最常見的選擇,也是Spring默認的事務傳播行為。

2.PROPAGATION_SUPPORTS

支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。

3.PROPAGATION_MANDATORY

支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就拋出異常。

4.PROPAGATION_REQUIRES_NEW

創建新事務,無論當前存不存在事務,都創建新事務。

5.PROPAGATION_NOT_SUPPORTED

以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

6.PROPAGATION_NEVER

以非事務方式執行,如果當前存在事務,則拋出異常。

7.PROPAGATION_NESTED

如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則按REQUIRED屬性執行。

其實這7中我也沒看懂,不過不急,咱們接下來直接看效果。

三、7種傳播行為實戰

演示前先建兩個表,用戶表和用戶角色表,一開始兩個表裡沒有數據。

需要注意下,為了數據更直觀,每次執行代碼時 先清空下user和user_role表的數據。

user表:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `des` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

user_role表:

CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `role_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1.PROPAGATION_REQUIRED測試

如果當前沒有事務,就創建一個新事務,如果當前存在事務,就加入該事務,這是最常見的選擇,也是Spring默認的事務傳播行為。

場景一:

此場景外圍方法沒有開啟事務。

1.驗證方法

兩個實現類UserServiceImpl和UserRoleServiceImpl制定事物傳播行為propagation=Propagation.REQUIRED,然後在測試方法中同時調用兩個方法並在調用結束后拋出異常。

2.主要代碼

外層調用方法代碼:

/**
     * 測試 PROPAGATION_REQUIRED
     *
     * @Author: java_suisui
     */
    @Test
    void test_PROPAGATION_REQUIRED() {
        // 增加用戶表
        User user = new User();
        user.setName("Java碎碎念");
        user.setPassword("123456");
        userService.add(user);
        // 增加用戶角色表
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(200);
        userRoleService.add(userRole);
        //拋異常
        throw new RuntimeException();
    }

UserServiceImpl代碼:

/**
     * 增加用戶
     */
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int add(User user) {
        return userMapper.add(user);
    }

UserRoleServiceImpl代碼:

    /**
     * 增加用戶角色
     */
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int add(UserRole userRole) {
        return userRoleMapper.add(userRole);
    }

3.代碼執行后數據庫截圖

兩張表數據都新增成功,截圖如下:

4.結果分析

外圍方法未開啟事務,插入用戶表和用戶角色表的方法在自己的事務中獨立運行,外圍方法異常不影響內部插入,所以兩條記錄都新增成功。

場景二:

此場景外圍方法開啟事務。

1.主要代碼

測試方法代碼如下:

/**
     * 測試 PROPAGATION_REQUIRED
     *
     * @Author: java_suisui
     */
    @Transactional
    @Test
    void test_PROPAGATION_REQUIRED() {
        // 增加用戶表
        User user = new User();
        user.setName("Java碎碎念");
        user.setPassword("123456");
        userService.add(user);
        // 增加用戶角色表
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(200);
        userRoleService.add(userRole);
        //拋異常
        throw new RuntimeException();
    }

2.代碼執行后數據庫截圖

兩張表數據都為空,截圖如下:

3.結果分析

外圍方法開啟事務,內部方法加入外圍方法事務,外圍方法回滾,內部方法也要回滾,所以兩個記錄都插入失敗。

結論:以上結果證明在外圍方法開啟事務的情況下Propagation.REQUIRED修飾的內部方法會加入到外圍方法的事務中,所以Propagation.REQUIRED修飾的內部方法和外圍方法均屬於同一事務,只要一個方法回滾,整個事務均回滾。

2.PROPAGATION_SUPPORTS測試

支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。

場景一:

此場景外圍方法沒有開啟事務。

1.驗證方法

兩個實現類UserServiceImpl和UserRoleServiceImpl制定事物傳播行為propagation=Propagation.SUPPORTS,然後在測試方法中同時調用兩個方法並在調用結束后拋出異常。

2.主要代碼

外層調用方法代碼:

    /**
     * 測試 PROPAGATION_SUPPORTS
     *
     * @Author: java_suisui
     */
    @Test
    void test_PROPAGATION_SUPPORTS() {
        // 增加用戶表
        User user = new User();
        user.setName("Java碎碎念");
        user.setPassword("123456");
        userService.add(user);
        // 增加用戶角色表
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(200);
        userRoleService.add(userRole);
        //拋異常
        throw new RuntimeException();
    }

UserServiceImpl代碼:

/**
     * 增加用戶
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public int add(User user) {
        return userMapper.add(user);
    }

UserRoleServiceImpl代碼:

    /**
     * 增加用戶角色
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public int add(UserRole userRole) {
        return userRoleMapper.add(userRole);
    }

3.代碼執行后數據庫截圖

兩張表數據都新增成功,截圖如下:

4.結果分析

外圍方法未開啟事務,插入用戶表和用戶角色表的方法以非事務的方式獨立運行,外圍方法異常不影響內部插入,所以兩條記錄都新增成功。

場景二:

此場景外圍方法開啟事務。

1.主要代碼

test_PROPAGATION_SUPPORTS方法添加註解@Transactional即可。

2.代碼執行后數據庫截圖

兩張表數據都為空,截圖如下:

3.結果分析

外圍方法開啟事務,內部方法加入外圍方法事務,外圍方法回滾,內部方法也要回滾,所以兩個記錄都插入失敗。

結論:以上結果證明在外圍方法開啟事務的情況下Propagation.SUPPORTS修飾的內部方法會加入到外圍方法的事務中,所以Propagation.SUPPORTS修飾的內部方法和外圍方法均屬於同一事務,只要一個方法回滾,整個事務均回滾。

3.PROPAGATION_MANDATORY測試

支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就拋出異常。

通過上面的測試,“支持當前事務,如果當前存在事務,就加入該事務”,這句話已經驗證了,外層添加@Transactional註解后兩條記錄都新增失敗,所以這個傳播行為只測試下外層沒有開始事務的場景。

場景一:

此場景外圍方法沒有開啟事務。

1.驗證方法

兩個實現類UserServiceImpl和UserRoleServiceImpl制定事物傳播行為propagation = Propagation.MANDATORY,主要代碼如下。

2.主要代碼

外層調用方法代碼:

    /**
     * 測試 PROPAGATION_MANDATORY
     *
     * @Author: java_suisui
     */
    @Test
    void test_PROPAGATION_MANDATORY() {
        // 增加用戶表
        User user = new User();
        user.setName("Java碎碎念");
        user.setPassword("123456");
        userService.add(user);
        // 增加用戶角色表
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(200);
        userRoleService.add(userRole);
        //拋異常
        throw new RuntimeException();
    }

UserServiceImpl代碼:

/**
     * 增加用戶
     */
    @Transactional(propagation = Propagation.MANDATORY)
    @Override
    public int add(User user) {
        return userMapper.add(user);
    }

UserRoleServiceImpl代碼:

    /**
     * 增加用戶角色
     */
    @Transactional(propagation = Propagation.MANDATORY)
    @Override
    public int add(UserRole userRole) {
        return userRoleMapper.add(userRole);
    }

3.代碼執行后數據庫截圖

兩張表數據都為空,截圖如下:

4.結果分析

運行日誌如下,可以發現在調用userService.add()時候已經報錯了,所以兩個表都沒有新增數據,驗證了“如果當前不存在事務,就拋出異常”。

at com.example.springboot.mybatisannotation.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$50090f18.add(<generated>)
    at com.example.springboot.mybatisannotation.SpringBootMybatisAnnotationApplicationTests.test_PROPAGATION_MANDATORY(SpringBootMybatisAnnotationApplicationTests.java:78)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

4.PROPAGATION_REQUIRES_NEW測試

創建新事務,無論當前存不存在事務,都創建新事務。

這種情況每次都創建事務,所以我們驗證一種情況即可。

場景一:

此場景外圍方法開啟事務。

1.驗證方法

兩個實現類UserServiceImpl和UserRoleServiceImpl制定事物傳播行為propagation = Propagation.REQUIRES_NEW,主要代碼如下。

2.主要代碼

外層調用方法代碼:

    /**
     * 測試 REQUIRES_NEW
     *
     * @Author: java_suisui
     */
    @Test
    @Transactional
    void test_REQUIRES_NEW() {
        // 增加用戶表
        User user = new User();
        user.setName("Java碎碎念");
        user.setPassword("123456");
        userService.add(user);
        // 增加用戶角色表
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(200);
        userRoleService.add(userRole);
        //拋異常
        throw new RuntimeException();
    }

UserServiceImpl代碼:

/**
     * 增加用戶
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public int add(User user) {
        return userMapper.add(user);
    }

UserRoleServiceImpl代碼:

    /**
     * 增加用戶角色
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public int add(UserRole userRole) {
        return userRoleMapper.add(userRole);
    }

3.代碼執行后數據庫截圖

兩張表數據都新增成功,截圖如下:

4.結果分析

無論當前存不存在事務,都創建新事務,所以兩個數據新增成功。

5.PROPAGATION_NOT_SUPPORTED測試

以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

場景一:

此場景外圍方法不開啟事務。

1.驗證方法

兩個實現類UserServiceImpl和UserRoleServiceImpl制定事物傳播行為propagation = Propagation.NOT_SUPPORTED,主要代碼如下。

2.主要代碼

外層調用方法代碼:

    /**
     * 測試 PROPAGATION_NOT_SUPPORTED
     *
     * @Author: java_suisui
     */
    @Test
    void test_PROPAGATION_NOT_SUPPORTED() {
        // 增加用戶表
        User user = new User();
        user.setName("Java碎碎念");
        user.setPassword("123456");
        userService.add(user);
        // 增加用戶角色表
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(200);
        userRoleService.add(userRole);
        //拋異常
        throw new RuntimeException();
    }

UserServiceImpl代碼:

/**
     * 增加用戶
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public int add(User user) {
        return userMapper.add(user);
    }

UserRoleServiceImpl代碼:

    /**
     * 增加用戶角色
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public int add(UserRole userRole) {
        return userRoleMapper.add(userRole);
    }

3.代碼執行后數據庫截圖

兩張表數據都新增成功,截圖如下:

4.結果分析

以非事務方式執行,所以兩個數據新增成功。

場景二:

此場景外圍方法開啟事務。

1.主要代碼

test_PROPAGATION_NOT_SUPPORTED方法添加註解@Transactional即可。

2.代碼執行后數據庫截圖

兩張表數據都新增成功,截圖如下:

3.結果分析

如果當前存在事務,就把當前事務掛起,相當於以非事務方式執行,所以兩個數據新增成功。

6.PROPAGATION_NEVER測試

以非事務方式執行,如果當前存在事務,則拋出異常。

上面已經有類似情況,外層沒有事務會以非事務的方式運行,兩個表新增成功;有事務則拋出異常,兩個表都都沒有新增數據。

7.PROPAGATION_NESTED測試

如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則按REQUIRED屬性執行。

上面已經有類似情況,外層沒有事務會以REQUIRED屬性的方式運行,兩個表新增成功;有事務但是用的是一個事務,方法最後拋出了異常導致回滾,兩個表都都沒有新增數據。

到此Spring的7種事務傳播行為已經全部介紹完成了,有問題歡迎留言溝通哦!

完整源碼地址: https://github.com/suisui2019/springboot-study

推薦閱讀

限時領取免費Java相關資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高併發分佈式、大數據、機器學習等技術。
關注下方公眾號即可免費領取:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務