深入了解Spring的事務傳播機制
Spring 事務傳播機制是指,包含多個事務的方法在相互調用時,事務是如何在這些方法間傳播的。
既然是“事務傳播”,所以事務的數(shù)量應該在兩個或兩個以上,Spring 事務傳播機制的誕生是為了規(guī)定多個事務在傳播過程中的行為的。比如方法 A 開啟了事務,而在執(zhí)行過程中又調用了開啟事務的 B 方法,那么 B 方法的事務是應該加入到 A 事務當中呢?還是兩個事務相互執(zhí)行互不影響,又或者是將 B 事務嵌套到 A 事務中執(zhí)行呢?所以這個時候就需要一個機制來規(guī)定和約束這兩個事務的行為,這就是 Spring 事務傳播機制所解決的問題。
Spring 事務傳播機制有哪些?
Spring 事務傳播機制可使用 @Transactional(propagation=Propagation.REQUIRED) 來定義,Spring 事務傳播機制的級別包含以下 7 種:
- Propagation.REQUIRED:默認的事務傳播級別,它表示如果當前存在事務,則加入該事務;如果當前沒有事務,則創(chuàng)建一個新的事務。
- Propagation.SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續(xù)運行。
- Propagation.MANDATORY:(mandatory:強制性)如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
- Propagation.REQUIRES_NEW:表示創(chuàng)建一個新的事務,如果當前存在事務,則把當前事務掛起。也就是說不管外部方法是否開啟事務,Propagation.REQUIRES_NEW 修飾的內部方法會新開啟自己的事務,且開啟的事務相互獨立,互不干擾。
- Propagation.NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- Propagation.NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
- Propagation.NESTED:如果當前存在事務,則創(chuàng)建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于 PROPAGATION_REQUIRED。
以上 7 種傳播機制,可根據(jù)“是否支持當前事務”的維度分為以下 3 類:
看到這里,有人可能會說:說了這么多,我也看不懂啊,即使看懂了,我也記不住?。窟@要咋整?
沒關系,接下來我們用一個例子,來說明這 3 類事務傳播機制的區(qū)別。
以情侶之間是否要買房為例,我們將以上 3 類事務傳播機制可以看作是戀愛中的 3 類女生類型:
- 普通型
- 強勢型
- 懂事型
這三類女生如下圖所示:
支持當前事務的“女生”,這里的事務指的是“房子”,它分為 3 種(普通型女生):
- Propagation.REQUIRED(需要有房子):有房子了咱們一起住,沒房子了咱們一起賺錢買房子。
- Propagation.SUPPORTS(可以有房子):有房子了就一起住,沒房子了咱們就一起租房子。
- Propagation.MANDATORY(強制有房子):有房子了就一起住,沒房子了就分手。
不支持當前事務的“女生”也分為 3 種(強勢型或者叫事業(yè)型):
- Propagation.REQUIRES_NEW:不要你的房子,必須一起賺錢買房子。
- Propagation.NOT_SUPPORTED:不要你的房子,必須一起租房子。
- Propagation.NEVER:必須一起租房子,你有房子就分手。
最后一種是嵌套性事務 Propagation.NESTED,它屬于懂事型女友,如果有房子了就以房子為基礎做點小生意,賣個花生、水果啥的,如果買賣成了,那就繼續(xù)發(fā)展;如果失敗了,至少還有房子;如果沒房子也沒關系,一起賺錢買房子。
事務傳播機制使用與演示
接下來我們演示一下事務傳播機制的使用,以下面 3 個最典型的事務傳播級別為例:
- 支持當前事務的 REQUIRED;
- 不支持當前事務的 REQUIRES_NEW;
- 嵌套事務 NESTED。
下來我們分別來看。
事務傳播機制的示例,需要用到以下兩張表:
-- 用戶表 CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL, `password` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL, `createtime` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; -- 日志表 CREATE TABLE `log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `content` text NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
創(chuàng)建一個 Spring Boot 項目,核心業(yè)務代碼有 3 個:UserController、UserServcie 以及 LogService。在 UserController 里面調用 UserService 添加用戶,并調用 LogService 添加日志。
REQUIRED 使用演示
REQUIRED 支持當前事務。
UserController 實現(xiàn)代碼如下,其中 save 方法開啟了事務:
@RestController public class UserController { @Resource private UserService userService; @Resource private LogService logService; @RequestMapping("/save") @Transactional public Object save(User user) { // 插入用戶操作 userService.save(user); // 插入日志 logService.saveLog("用戶插入:" + user.getName()); return true; } }
UserService 實現(xiàn)代碼如下:
@Service public class UserService { @Resource private UserMapper userMapper; @Transactional(propagation = Propagation.REQUIRED) public int save(User user) { return userMapper.save(user); } }
LogService 實現(xiàn)代碼如下:
@Service public class LogService { @Resource private LogMapper logMapper; @Transactional(propagation = Propagation.REQUIRED) public int saveLog(String content) { // 出現(xiàn)異常 int i = 10 / 0; return logMapper.saveLog(content); } }
執(zhí)行結果:程序報錯,兩張表中都沒有插入任何數(shù)據(jù)。
執(zhí)行流程描述:
- 首先 UserService 中的添加用戶方法正常執(zhí)行完成。
- LogService 保存日志程序報錯,因為使用的是 UserController 中的全局事務,所以整個事務回滾,步驟 1 中的操作也跟著回滾。
- 所以數(shù)據(jù)庫中沒有添加任何數(shù)據(jù)。
REQUIRED_NEW 使用演示
REQUIRED_NEW 不支持當前事務。
UserController 實現(xiàn)代碼:
@RequestMapping("/save") @Transactional public Object save(User user) { // 插入用戶操作 userService.save(user); // 插入日志 logService.saveLog("用戶插入:" + user.getName()); return true; }
UserService 實現(xiàn)代碼:
@Service public class UserService { @Resource private UserMapper userMapper; @Transactional(propagation = Propagation.REQUIRES_NEW) public int save(User user) { System.out.println("執(zhí)行 save 方法."); return userMapper.save(user); } }
LogService 實現(xiàn)代碼:
@Service public class LogService { @Resource private LogMapper logMapper; @Transactional(propagation = Propagation.REQUIRES_NEW) public int saveLog(String content) { // 出現(xiàn)異常 int i = 10 / 0; return logMapper.saveLog(content); } }
程序執(zhí)行結果:
User 表中成功添加了一條用戶數(shù)據(jù),Log 表執(zhí)行失敗,沒有加入任何數(shù)據(jù),但它并沒有影響到 UserController 中的事務執(zhí)行。
通過以上結果可以看出:LogService 中使用的是單獨的事務,雖然 LogService 中的事務執(zhí)行失敗了,但并沒有影響 UserController 和 UserService 中的事務。
NESTED 使用演示
NESTED 是嵌套事務。
UserController 實現(xiàn)代碼如下:
@RequestMapping("/save") @Transactional public Object save(User user) { // 插入用戶操作 userService.save(user); return true; }
UserService 實現(xiàn)代碼如下:
@Transactional(propagation = Propagation.NESTED) public int save(User user) { int result = userMapper.save(user); System.out.println("執(zhí)行 save 方法."); // 插入日志 logService.saveLog("用戶插入:" + user.getName()); return result; }
LogService 實現(xiàn)代碼如下:
@Transactional(propagation = Propagation.NESTED) public int saveLog(String content) { // 出現(xiàn)異常 int i = 10 / 0; return logMapper.saveLog(content); }
最終執(zhí)行結果,用戶表和日志表都沒有添加任何數(shù)據(jù)。
執(zhí)行流程描述:
- UserController 中調用了 UserService 的添加用戶方法,UserService 使用 NESTED 循環(huán)嵌套事務,并成功執(zhí)行了添加用戶的方法。
- UserService 中調用了 LogService 的添加方法,LogService 使用了 NESTED 循環(huán)嵌套事務,但在方法執(zhí)行中出現(xiàn)的異常,因此回滾了當前事務。
- 因為 UserService 使用的是嵌套事務,所以發(fā)生回滾的事務是全局的,也就是說 UserService 中的添加用戶方法也被回滾了,最終執(zhí)行結果是用戶表和日志表都沒有添加任何數(shù)據(jù)。
總結
Spring 事務傳播機制是包含多個事務的方法在相互調用時,事務是如何在這些方法間傳播的。事務的傳播級別有 7 個,支持當前事務的:REQUIRED、SUPPORTS、MANDATORY;不支持當前事務的:REQUIRES_NEW、NOT_SUPPORTED、NEVER,以及嵌套事務 NESTED,其中 REQUIRED 是默認的事務傳播級別。
以上就是深入了解Spring的事務傳播機制的詳細內容,更多關于Spring事務傳播機制的資料請關注腳本之家其它相關文章!
相關文章
在Spring?MVC中使用@ControllerAdvice創(chuàng)建全局異常處理器的方法
在Spring?MVC中,可以使用@ControllerAdvice或@RestControllerAdvice注解來定義全局異常處理器類,并使用?@ExceptionHandler注解來定義處理特定異常的方法,本文就給大家介紹了Spring?MVC?@ControllerAdvice創(chuàng)建處理器的方法,需要的朋友可以參考下2023-08-08使用SpringBoot-JPA進行自定義保存及批量保存功能
這篇文章主要介紹了使用SpringBoot-JPA進行自定義的保存及批量保存功能,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-06-06java中toString()、String.valueOf()、(String)?強轉的區(qū)別
在實際開發(fā)中,少不了使用這三種方法對某一個類型的數(shù)據(jù)進行轉?String?的操作,本文就來介紹了java中toString()、String.valueOf()、(String)?強轉的區(qū)別,感興趣的可以了解一下2024-06-06