本文介紹Spring的七種事務傳播行爲並經過代碼演示下。java
事務傳播行爲(propagation behavior)指的就是當一個事務方法被另外一個事務方法調用時,這個事務方法應該如何運行。git
例如:methodA方法調用methodB方法時,methodB是繼續在調用者methodA的事務中運行呢,仍是爲本身開啓一個新事務運行,這就是由methodB的事務傳播行爲決定的。github
Spring在TransactionDefinition接口中規定了7種類型的事務傳播行爲。事務傳播行爲是Spring框架獨有的事務加強特性。這是Spring爲咱們提供的強大的工具箱,使用事務傳播行爲能夠爲咱們的開發工做提供許多便利。spring
7種事務傳播行爲以下:數據庫
1.PROPAGATION_REQUIREDspringboot
若是當前沒有事務,就建立一個新事務,若是當前存在事務,就加入該事務,這是最多見的選擇,也是Spring默認的事務傳播行爲。bash
2.PROPAGATION_SUPPORTSmybatis
支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就以非事務執行。併發
3.PROPAGATION_MANDATORYapp
支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就拋出異常。
4.PROPAGATION_REQUIRES_NEW
建立新事務,不管當前存不存在事務,都建立新事務。
5.PROPAGATION_NOT_SUPPORTED
以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
6.PROPAGATION_NEVER
以非事務方式執行,若是當前存在事務,則拋出異常。
7.PROPAGATION_NESTED
若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則按REQUIRED屬性執行。
其實這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;
複製代碼
若是當前沒有事務,就建立一個新事務,若是當前存在事務,就加入該事務,這是最多見的選擇,也是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修飾的內部方法和外圍方法均屬於同一事務,只要一個方法回滾,整個事務均回滾。
支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就以非事務執行。
場景一:
此場景外圍方法沒有開啓事務。
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修飾的內部方法和外圍方法均屬於同一事務,只要一個方法回滾,整個事務均回滾。
支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就拋出異常。
經過上面的測試,「支持當前事務,若是當前存在事務,就加入該事務」,這句話已經驗證了,外層添加@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)
複製代碼
建立新事務,不管當前存不存在事務,都建立新事務。
這種狀況每次都建立事務,因此咱們驗證一種狀況便可。
場景一:
此場景外圍方法開啓事務。
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.結果分析
不管當前存不存在事務,都建立新事務,因此兩個數據新增成功。
以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
場景一:
此場景外圍方法不開啓事務。
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.結果分析
若是當前存在事務,就把當前事務掛起,至關於以非事務方式執行,因此兩個數據新增成功。
以非事務方式執行,若是當前存在事務,則拋出異常。
上面已經有相似狀況,外層沒有事務會以非事務的方式運行,兩個表新增成功;有事務則拋出異常,兩個表都都沒有新增數據。
若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則按REQUIRED屬性執行。
上面已經有相似狀況,外層沒有事務會以REQUIRED屬性的方式運行,兩個表新增成功;有事務可是用的是一個事務,方法最後拋出了異常致使回滾,兩個表都都沒有新增數據。
到此Spring的7種事務傳播行爲已經所有介紹完成了,有問題歡迎留言溝通哦!
完整源碼地址: github.com/suisui2019/…
推薦閱讀
1.SpringBoot系列-整合Mybatis(註解方式)
2.SpringBoot系列-整合Mybatis(XML配置方式)
限時領取免費Java相關資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高併發分佈式、大數據、機器學習等技術。 關注下方公衆號便可免費領取: