1. 為什么需要事務(wù)?
事務(wù)定義
將一組操作封裝成一個執(zhí)行單元 (封裝到一起),這一組的執(zhí)行具備原子性, 那么就要么全部成功,要么全部失敗.
為什么要用事務(wù)?
比如轉(zhuǎn)賬分為兩個操作:
第一步操作:A 賬戶-100 元。
第二步操作:B賬戶 +100 元。
如果沒有事務(wù),第一步執(zhí)行成功了,第二步執(zhí)行失敗了,那么 A 賬戶平白無故的 100 元就“人間蒸發(fā)”了。而如果使用事務(wù)就可以解決這個問題,讓這一組操作要么一起成功,要么一起失敗。
2. Spring中事務(wù)的實現(xiàn)
Spring中的事務(wù)操作分為兩類:
- 編程式事務(wù)(手動寫代碼操作事務(wù))
- 聲明式事務(wù)(利用注解自動開啟和提交事務(wù))
在開始講解它們之前,咱們先來回顧事務(wù)在MSQL 中是如何使用的?
2.1 MySQL中的事務(wù)使用
事務(wù)在 MySQL 有 3個重要的操作: 開啟事務(wù)、提交事務(wù)、回滾事務(wù),它們對應(yīng)的操作命令如下:
-- 開啟事務(wù)
start transaction;
-- 業(yè)務(wù)執(zhí)行
-- 提交事務(wù)
commit;
-- 回滾事務(wù)
rollback;
2.2 Spring 編程式事務(wù)(手動)
Spring 手動操作事務(wù)和上面MySQL 操作事務(wù)類似,它也是有3個重要操作步驟:
- 開啟事務(wù)(獲取事務(wù))
- 提交事務(wù)
- 回滾事務(wù)
SpringBoot 內(nèi)置了兩個對象,DataSourceTransactionManager
用來獲取事務(wù)(開啟事務(wù))、提交或回滾事務(wù)的,而 TransactionDefinition
是事務(wù)的屬性,在獲取事務(wù)的時候需要將TransactionDefinition
傳遞進去從而獲得一個事務(wù) TransactionStatus
,實現(xiàn)代碼如下:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// 編程式事務(wù)
// JDBC 事務(wù)管理器
@Autowired
private DataSourceTransactionManager transactionManager;
// 定義事務(wù)屬性 (使用TransactionDefinition記錄事務(wù)屬性)
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/del")
public int del(Integer id) {
if (id == null || id <= 0) return 0;
// 1. 開啟事務(wù)
TransactionStatus transactionStatus = null;
int result = 0;
try {
transactionStatus = transactionManager.getTransaction(transactionDefinition);
// 業(yè)務(wù)操作: 刪除用戶
result = userService.del(id);
System.out.println("刪除: " + result);
// 2. 提交事務(wù) / 回滾事務(wù)
// transactionManager.commit(transactionStatus); // 提交事務(wù)
} catch (Exception e) {
if (transactionStatus != null) {
transactionManager.rollback(transactionStatus); // 回滾事務(wù)
}
}
return result;
}
}
業(yè)務(wù)操作相關(guān)代碼如下:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public int del(Integer id){
return userMapper.del(id);
}
}
@Mapper
public interface UserMapper {
int del(@Param("id") Integer id);
}
<delete id="del">
delete from userinfo where id=#{id}
</delete>
運行結(jié)果:
可以看到, 刪除的業(yè)務(wù)操作已經(jīng)成功了, 但是因為回滾操作, 所以在執(zhí)行前后查詢數(shù)據(jù)庫的時候是可以看到對應(yīng)的數(shù)據(jù)依然存在.
從上述代碼可以看出,以上代碼雖然可以實現(xiàn)事務(wù),但操作也很繁瑣,有沒有更簡單的實現(xiàn)方法呢?請看下面聲明式事務(wù)。
2.3 Spring 聲明式事務(wù) (自動)
聲明式事務(wù)的實現(xiàn)很簡單,只需要在需要的方法上添加 @Transactional 注解就可以實現(xiàn)了,無需手動開啟事務(wù)和提交事務(wù),進入方法時自動開啟事務(wù),方法執(zhí)行完會自動提交事務(wù),如果中途發(fā)生了沒有處理的異常會自動回滾事務(wù),具體實現(xiàn)代碼如下:
@RestController
@RequestMapping("/user2")
public class UserController2 {
@Autowired
private UserService userService;
@Transactional // 在方法開始之前開啟事務(wù), 方法正常執(zhí)行結(jié)束之后提交事務(wù), 如果執(zhí)行途中發(fā)生異常, 則回滾事務(wù).
@RequestMapping("/del")
public int del(Integer id) {
if (id == null || id <= 0) return 0;
return userService.del(id);
}
}
執(zhí)行前后查詢數(shù)據(jù)庫, 可以看到由于正常執(zhí)行所以沒有觸發(fā)回滾.
2.3.1 @Transactional作用范圍
@Transactional 可以用來修飾方法或類
- 修飾方法時:需要注意只能應(yīng)用到 public 方法上,否則不生效。推薦此種用法
- 修飾類時:表明該注解對該類中所有的 public 方法都生效。
2.3.2 @Transactional 參數(shù)說明
參數(shù) |
作用 |
---|---|
value |
當(dāng)配置了多個事務(wù)管理器時,可以使用該屬性指定選擇哪個事務(wù)管理器 |
transactionManager |
當(dāng)配置了多個事務(wù)管理器時,可以使用該屬性指定選擇哪個事務(wù)管理器 |
propagation |
事務(wù)的傳播行為,默認值為 Propagation.REQUIRED |
isolation |
事務(wù)的隔離級別.默認值為 Isolation.DEFAULT |
timeout |
事務(wù)的超時時間,默認值為-1. 如果超過該時間限制但事務(wù)還沒有完成,則自動回滾事務(wù). |
readOnly |
指定事務(wù)是否為只讀事務(wù).默認值為 false; 為了忽略那些不需要事務(wù)的方法,比如讀取數(shù)據(jù)可以設(shè)置read-only為 true. |
rollbackFor |
用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個異常類型 |
rollbackForClassName |
用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個異常類型 |
noRollbackFor |
拋出指定的異常類型,不回滾事務(wù),也可以指定多個異常類型 |
noRollbackForClassName |
拋出指定的異常類型,不回滾事務(wù),也可以指定多個異常類型 |
2.3.3 注意事項
@Transactional 在異常被捕獲的情況下,不會進行事務(wù)自動回滾,驗證以下代碼是否會發(fā)生事務(wù)回滾:
@RestController
@RequestMapping("/user2")
public class UserController2 {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/del")
public int del(Integer id) {
if (id == null || id <= 0) return 0;
int result = userService.del(id);
System.out.println("刪除: " + result);
try {
int num = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return result;
}
}
執(zhí)行前后查詢數(shù)據(jù)庫, 可以看到在異常被捕獲的情況下,并不會進行事務(wù)自動回滾.
事務(wù)不會自動回滾解決方案
解決方案1
對于捕獲的異常,事務(wù)是會自動回滾的,因此解決方案1就是可以將異常重新拋出,具體實現(xiàn)如下:
@RestController
@RequestMapping("/user2")
public class UserController2 {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/del")
public int del(Integer id) {
if (id == null || id <= 0) return 0;
int result = userService.del(id);
System.out.println("刪除: " + result);
try {
int num = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
throw e;
}
return result;
}
}
執(zhí)行前后查詢數(shù)據(jù)庫, 可以看到這里是有異常, 觸發(fā)了回滾.
解決方案2
手動回滾事務(wù),在方法中使用 TransactionAspectSupport.currentTransactionStatus() 可以得到當(dāng)前的事務(wù),然后設(shè)置回滾方法 setRollbackOnly 就可以實現(xiàn)回滾了,具體實現(xiàn)代碼如下:
public class UserController2 {
// .. 省略代碼, 同上
try {
int num = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
// 手動回滾事務(wù)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
}
執(zhí)行前后訪問數(shù)據(jù)庫可以看到, 這里成功實現(xiàn)了回滾操作.
2.3.4 @Transactional 工作原理
@Transactional 是基于 AOP 實現(xiàn)的,AOP 又是使用動態(tài)代理實現(xiàn)的。如果目標(biāo)對象實現(xiàn)了接口,默認情況下會采用JDK 的動態(tài)代理,如果目標(biāo)對象沒有實現(xiàn)了接口,會使用CGLIB 動態(tài)代理。
@Transactional 在開始執(zhí)行業(yè)務(wù)之前,通過代理先開啟事務(wù),在執(zhí)行成功之后再提交事務(wù)。如果中途遇到的異常,則回滾事務(wù)。
@Transactional 實現(xiàn)思路預(yù)覽:
@Transactional 具體執(zhí)行細節(jié)如下圖所示:
3. 事務(wù)隔離級別
3.1 事務(wù)特性回顧
事務(wù)有4 大特性 (ACID),原子性、持久性、一致性和隔離性,具體概念如下:
- 原子性: 一個事務(wù)(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾 (Rollback)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。
- 一致性: 在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)規(guī)則,這包含資料的精確度、串聯(lián)性以及后續(xù)數(shù)據(jù)庫可以自發(fā)性地完成預(yù)定的工作。
- 持久性: 事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。
- 隔離性: 數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時對其數(shù)據(jù)進行讀寫和修改的能力,隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級別,包括讀未提交 (Read uncommitted)、讀提交 (read committed) 、可重復(fù)讀 (repeatable read) 和串行化(Serializable)。
上面 4個屬性,可以簡稱為ACID
原子性 (Atomicity,或稱不可分割性)
一致性 (Consistency)
隔離性 (lsolation,又稱獨立性)
持久性 (Durability)
而這 4 種特性中,只有隔離性 (隔離級別) 是可以設(shè)置的。
為什么要設(shè)置事務(wù)的隔離級別?
設(shè)置事務(wù)的隔離級別是用來保障多個并發(fā)事務(wù)執(zhí)行更可控,更符合操作者預(yù)期的。
什么是可控呢?
比如近幾年比較嚴重的新冠病毒,我們會把直接接觸到確診病例的人員隔離到酒店,而把間接接觸者 (和直接接觸著但未確診的人) 隔離在自己的家中,也就是針對不同的人群,采取不同的隔離級別,這種隔離方式就和事務(wù)的隔離級別類似,都是采取某種行動讓某個事件變的“更可控”。而事務(wù)的隔離級別就是為了防止,其他的事務(wù)影響當(dāng)前事務(wù)執(zhí)行的一種策略。
3.2 Spring 中設(shè)置事務(wù)隔離級別
Spring 中事務(wù)隔離級別可以通過 @Transactional 中的 isolation 屬性進行設(shè)置,具體操作如下圖所示:
3.2.1 MySQL事務(wù)隔離級別有 4 種
- READ UNCOMMITTED: 讀未提交,也叫未提交讀,該隔離級別的事務(wù)可以看到其他事務(wù)中未提交的數(shù)據(jù)。該隔離級別因為可以讀取到其他事務(wù)中未提交的數(shù)據(jù),而未提交的數(shù)據(jù)可能會發(fā)生回滾,因此我們把該級別讀取到的數(shù)據(jù)稱之為臟數(shù)據(jù),把這個問題稱之為臟讀。
- READ COMMITTED: 讀已提交,也叫提交讀,該隔離級別的事務(wù)能讀取到已經(jīng)提交事務(wù)的數(shù)據(jù),因此它不會有臟讀問題。但由于在事務(wù)的執(zhí)行中可以讀取到其他事務(wù)提交的結(jié)果,所以在不同時間的相同 SQL查詢中,可能會得到不同的結(jié)果,這種現(xiàn)象叫做不可重復(fù)讀。
- REPEATABLE READ: 可重復(fù)讀,是MySQL的默認事務(wù)隔離級別,它能確保同一事務(wù)多次查詢的結(jié)果一致。但也會有新的問題,比如此級別的事務(wù)正在執(zhí)行時,另一個事務(wù)成功的插入了某條數(shù)據(jù),但因為它每次查詢的結(jié)果都是一樣的,所以會導(dǎo)致查詢不到這條數(shù)據(jù),自己重復(fù)插入時又失敗(因為唯一約束的原因)。明明在事務(wù)中查詢不到這條信息,但自己就是插入不進去,這就叫幻讀(Phantom Read)
- SERIALIZABLE: 序列化,事務(wù)最高隔離級別,它會強制事務(wù)排序,使之不會發(fā)生沖突,從而解決了臟讀、不可重復(fù)讀和幻讀問題,但因為執(zhí)行效率低,所以真正使用的場景并不多
1. 臟讀: 一個事務(wù)讀取到了另一個事務(wù)修改的數(shù)據(jù)之后,后一個事務(wù)又進行了回滾操作,從而導(dǎo)致第一個事務(wù)讀取的數(shù)據(jù)是錯誤的。
2. 不可重復(fù)讀: 一個事務(wù)兩次查詢得到的結(jié)果不同,因為在兩次查詢中間,有另一個事務(wù)把數(shù)據(jù)修改了。
3. 幻讀: 一個事務(wù)兩次查詢中得到的結(jié)果集不同,因為在兩次查詢中另一個事務(wù)有新增了一部分數(shù)據(jù)。
在數(shù)據(jù)庫中通過以下SQL 查詢?nèi)质聞?wù)隔離級別和當(dāng)前連接的事務(wù)隔離級別:
select @@global.tx_isolation,@@tx_isolation;
以上SQL的執(zhí)行結(jié)果如下:
3.2.2 Spring 中事務(wù)隔離級別有 5 種
Spring 中事務(wù)隔離級別包含以下 5 種:
- Isolation.DEFAULT: 以連接的數(shù)據(jù)庫的事務(wù)隔離級別為主
- lsolation.READ_UNCOMMITTED: 讀未提交,可以讀取到未提交的事務(wù),存在臟讀
- Isolation.READ_COMMITTED: 讀已提交,只能讀取到已經(jīng)提交的事務(wù),解決了臟讀,存在不可重復(fù)讀。
- Isolation.REPEATABLE_READ: 可重復(fù)讀,解決了不可重復(fù)讀,但存在幻讀(MVSQL默認級
- lsolation.SERIALIZABLE: 串行化,可以解決所有并發(fā)問題,但性能太低。
從上述介紹可以看出,相比于 MySQL 的事務(wù)隔離級別,Spring 的事務(wù)隔離級別只是多了一個lsolation.DEFAULT (以數(shù)據(jù)庫的全局事務(wù)隔離級別為主)。
Spring 中事務(wù)隔離級別只需要設(shè)置 @Transactional 里的 isolation 屬性即可,具體實現(xiàn)代碼如下:
@RequestMapping("/save")
@Transactional(isolation = Isolation.SERIALIZABLE)
public Object save(User user) {
// 業(yè)務(wù)實現(xiàn)
}
4. Spring 事務(wù)傳播機制
4.1 事務(wù)傳播機制是什么?
Spring 事務(wù)傳播機制定義了多個包含了事務(wù)的方法,相互調(diào)用時,事務(wù)是如何在這些方法間進行傳遞的。
4.2 為什么需要事務(wù)傳播機制?
事務(wù)隔離級別是保證多個并發(fā)事務(wù)執(zhí)行的可控性的(穩(wěn)定性的),而事務(wù)傳播機制是保證一個事務(wù)在多個調(diào)用方法間的可控性的 (穩(wěn)定性的)。
例子: 像新冠病毒一樣,它有不同的隔離方式(酒店隔離還是居家隔離),是為了保證疫情可控,然而在每個人的隔離過程中,會有很多個執(zhí)行的環(huán)節(jié),比如酒店隔離,需要負責(zé)人員運送、物品運送消殺原生活區(qū)域、定時核算檢查和定時送餐等很多環(huán)節(jié),而事務(wù)傳播機制就是保證一個事務(wù)在傳遞過程中是可靠性的,回到本身案例中就是保證每個人在隔離的過程中可控的。
事務(wù)隔離級別解決的是多個事務(wù)同時調(diào)用一個數(shù)據(jù)庫的問題,如下圖所示:
而事務(wù)傳播機制解決的是一個事務(wù)在多個節(jié)點(方法) 中傳遞的問題,如下圖所示:
4.3 事務(wù)傳播機制有哪些?
Spring 事務(wù)傳播機制包含以下 7種:
- Propagation.REQUIRED: 默認的事務(wù)傳播級別,它表示如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。
- Propagation.SUPPORTS: 如果當(dāng)前存在事務(wù),則加入該事務(wù); 如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。
- Propagation.MANDATORY: (mandatory: 強制性) 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
- Propagation.REQUIRES_NEW: 表示創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。也就是說不管外部方法是否開啟事務(wù),Propagation.REQUIRES_NEW 修飾的內(nèi)部方法會新開啟自己的事務(wù),且開啟的事務(wù)相互獨立,互不干擾。
- Propagation.NOT_SUPPORTED: 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
- Propagation.NEVER: 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常
- Propagation.NESTED: 如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行;如果當(dāng)前沒有事務(wù),則該取值等價于 PROPAGATION_REQUIRED。
以上 7 種傳播行為,可以根據(jù)是否支持當(dāng)前事務(wù)分為以下 3類:
以情侶關(guān)系為例來理解以上分類:
4.4 Spring 事務(wù)傳播機制使用和各種場景演示
4.4.1 支持當(dāng)前事務(wù)(REQUIRED)
我們有多個方法進行數(shù)據(jù)的傳遞.
不考慮攔截器, 考慮標(biāo)準的分層, 比如要執(zhí)行用戶User的添加add操作, 下圖為該執(zhí)行流程圖
首先要執(zhí)行add, 就會先在UserController加@Transactional, 下面為了演示事務(wù)的傳播機制, 就需要讓用戶調(diào)用UserService, 并加上@Transactional.
在UserService中, 我們需要操作兩張表, 先調(diào)用UserMapper中的add添加方法, 這個方法是添加用戶的, 再調(diào)用日志添加類.
從上圖可見, @Transactional是通過UserController傳遞到UserService, 再從UserService傳遞到LogService.
注意, 正常來說在*Service要調(diào)用*Mapper類, 但是這里如果使用LogMapper是Interface, 就無法演示清楚預(yù)期效果, 所以這里調(diào)用LogService.
在mycnblog中創(chuàng)建日志表.
CREATE TABLE `log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`message` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
在項目中創(chuàng)建實體類:
@Data
public class UserInfo {
private int id;
private String username;
private String password;
private String photo;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private int state;
}
@Data
public class Log {
private int id;
private LocalDateTime timestamp;
private String message;
}
演示事務(wù)傳播機制代碼實現(xiàn):
User
@RestController
@RequestMapping("/user3")
public class UserController3 {
@Autowired
private UserService userService;
@RequestMapping("/add")
@Transactional(propagation = Propagation.REQUIRED)
public int add(String username, String password){
if (null == username || null == password ||
username.equals("") || password.equals("")) return 0;
UserInfo user = new UserInfo();
user.setUsername(username);
user.setPassword(password);
int result = userService.add(user);
// 用戶添加操作
return 0;
}
}
UserService 實現(xiàn)代碼如下:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private LogService logService;
@Transactional(propagation = Propagation.REQUIRED)
public int add(UserInfo userInfo) {
// 給用戶表添加用戶信息
int addUserResult = userMapper.add(userInfo);
System.out.println("添加用戶結(jié)果: " + addUserResult);
// 添加日志信息
Log log = new Log();
log.setMessage("添加用戶信息");
logService.add(log);
return addUserResult;
}
}
在UserMapper中寫添加add方法.
@Mapper
public interface UserMapper {
int add(UserInfo userInfo);
}
在xml中寫具體的實現(xiàn):
<insert id="add">
insert into userinfo(username, password) values
(#{username},#{password})
</insert>
Log
LogMapper.interface
@Mapper
public interface LogMapper {
int add(Log log);
}
LogMapper.xml
<insert id="add">
insert into log(`message`) values(#{message})
</insert>
LogService:
@Service
public class LogService {
@Autowired
private LogMapper logMapper;
@Transactional(propagation = Propagation.REQUIRED)
public int add(Log log) {
int result = logMapper.add(log);
System.out.println("添加日志結(jié)果: " + result);
// 回滾操作
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
int num = 10 / 0;
return result;
}
}
接下來我們觀察一下在UserService中已經(jīng)執(zhí)行成功的user添加操作有沒有回滾事務(wù), 如果user中沒有正常添加數(shù)據(jù), 那么就說明REQUIRED是支持當(dāng)前的事務(wù), 并且是以加入的方式去進行的.
可以看到, 添加用戶和添加日志都已經(jīng)成功了, 最終有一個算數(shù)異常報出. 我們再來看一下數(shù)據(jù)庫.
可以看到, log回滾了, 符合預(yù)期, 然而userinfo中并沒有將前面url所傳數(shù)據(jù)wangwu添加過來, 那么這就說明, REQUIRED這種事務(wù)傳播機制是支持當(dāng)前事務(wù)的, 并且是加入事務(wù)的方式, 讓自己變成事務(wù)的一部分, 如果其中有任何一個地方出現(xiàn)問題, 不論前面做了多少業(yè)務(wù), 那么整條調(diào)用鏈上所有的方法都會進行回滾.
4.4.2 不支持當(dāng)前事務(wù)(REQUIRESNEW)
將整條事務(wù)調(diào)用鏈上的所有方法修改為 REQUIRES_NEW不支持當(dāng)前事務(wù),重新創(chuàng)建事務(wù),觀察執(zhí)行結(jié)果:
@Transactional(propagation = Propagation.REQUIRES_NEW)
預(yù)期執(zhí)行結(jié)果: 在一個調(diào)用鏈上的事務(wù),各自執(zhí)行相互不干擾。
對上面的示例來說, 是用戶添加 (事務(wù)) 成功、但是日志添加 (事務(wù)) 失敗。
可以看到, 我們的執(zhí)行結(jié)果并沒有符合我們的預(yù)計結(jié)果.
原因是由于沒有處理 by zero的異常, 導(dǎo)致整個調(diào)用鏈上的其他對象都感知到了, 所以進行了回滾.
此時修改LogService代碼:
@Service
public class LogService {
@Autowired
private LogMapper logMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int add(Log log) {
int result = logMapper.add(log);
System.out.println("添加日志結(jié)果: " + result);
// 回滾操作
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return result;
}
}
這個時候便不會報錯了.
可以看到, 用戶和日志都添加成功了, 但是日志中進行了手動回滾.
驗證一下:
說明符合了預(yù)期.文章來源:http://www.zghlxwxcb.cn/news/detail-667754.html
4.4.3 嵌套事務(wù) (NESTED) 和加入事務(wù) (REQUIRED )的區(qū)別
- 整個事務(wù)如果全部執(zhí)行成功,二者的結(jié)果是一樣的。
- 如果事務(wù)執(zhí)行到一半失敗了,那么加入事務(wù)整個事務(wù)會全部回滾;而嵌套事務(wù)會局部回滾,不會影響上一個方法中執(zhí)行的結(jié)果
至此, 整個JavaEE專欄的知識介紹就到這里.文章來源地址http://www.zghlxwxcb.cn/news/detail-667754.html
到了這里,關(guān)于Spring Boot 事務(wù)和事務(wù)傳播機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!