詳解Spring事務(wù)和事務(wù)傳播機制
1. 事務(wù)的回顧
在 MySQL 學(xué)習(xí)階段,已經(jīng)了解到了事務(wù)是一組操作的集合,也就是把所有的操作作為一個整體,一起向數(shù)據(jù)庫提交或者撤銷操作,要么同時成功,要么同時失敗
一個事務(wù)的操作流程包括了,開啟事務(wù),執(zhí)行事務(wù)操作,提交事務(wù)或回滾事務(wù),對于回滾事務(wù)來說,如果程序在執(zhí)行過程中出現(xiàn)了錯誤,那么此時就需要執(zhí)行回滾事務(wù)
2. 事務(wù)的實現(xiàn)方式
2.1. 編程式事務(wù)
Spring 手動操作事務(wù)和 MySQL 操作事務(wù)類似,也是分為開啟事務(wù),提交事務(wù),回滾事務(wù)等三個操作,需要用到 DataSourceTransactionManager (事務(wù)管理器)來進(jìn)行上述事務(wù)的操作,還需要用到 TransactionDefinition(事務(wù)的屬性,獲取事務(wù)時需要把這個類的對象傳進(jìn)去)
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/registy")
public String registy(String name, String password) {
//開啟事務(wù),獲取一個狀態(tài),之后回滾就回滾到了這個狀態(tài)
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
Integer reuslt = userService.insert(name, password);
//提交事務(wù)(提交的是之前獲取的狀態(tài))
dataSourceTransactionManager.commit(transaction);
return "注冊成功";
}
}
測試之后數(shù)據(jù)也是正常更新了
回滾的話調(diào)用的是 rollback 方法,再次進(jìn)行插入數(shù)據(jù),數(shù)據(jù)就沒有更新,不過自增 id 還是變成了 3,對比提交事務(wù)的日志可以看出,這次沒有提交事務(wù)的信息了

2.2. 聲明式事務(wù)
上面的方式是比較麻煩的,需要自己寫一大堆信息,來看聲明式事務(wù)是如何操作的
首先需要添加依賴:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency>
只需在要執(zhí)行的方法上添加@Transactional注解,添加之后,如果沒有發(fā)生異常就正常執(zhí)行,如果發(fā)生了異常就回滾事務(wù)

來看異常的情況:

這時事務(wù)就沒有提交,進(jìn)行了回滾
3. @Transactional
@Transactional 可以用來修飾方法或類,修飾方法時,只有修飾 public 方法時才生效,修飾其他方法時不會報錯,但也不生效,修飾類時,對該類中所有的 public 方法都生效
在目標(biāo)方法執(zhí)行開始之前會自動開啟事務(wù),執(zhí)行結(jié)束之后會自動提交事務(wù),如果方法執(zhí)行過程中出現(xiàn)異常且異常未被捕獲,就進(jìn)行事務(wù)回滾操作
例如,把上面的異常代碼 catch 起來,事務(wù)就正常提交了


但是如果捕獲之后又進(jìn)行拋出,那么事務(wù)還是會回滾的


還可以通過調(diào)用 setRollbackOnly 方法進(jìn)行手動回滾

這樣的話把異常捕獲之后還可以回滾事務(wù)
3.1. rollbackFor
@Transactional默認(rèn)只在遇到 RuntimeException 和 Error 時才進(jìn)行回滾,非運行時異常就不會滾,來演示一下發(fā)生非運行異常時的情況:


雖然此時拋出了異常,但是事務(wù)還是提交了,并沒有進(jìn)行回滾,可以通過設(shè)置@Transactional注解的 rollbackFor 屬性來指定那些異常要回滾
把 rollbackFor 設(shè)置為 Exception.class,表示 Exception 底下的子類異常都會發(fā)生回滾
@Transactional(rollbackFor = Exception.class)
@RequestMapping("/r3")
public String r3(String name, String password) throws IOException {
Integer reuslt = userService.insert(name, password);
if (true) {
throw new IOException();
}
return "注冊成功";
}此時再次測試,事務(wù)就回滾了

3.2. isolation
@Transactional 注解的 isolation 屬性是可以設(shè)置事務(wù)的隔離級別的,參數(shù)類型是一個 Isolation 的枚舉類,依次表示當(dāng)前數(shù)據(jù)庫默認(rèn)使用的隔離級別和事務(wù)的四種隔離級別

可以根據(jù)需要進(jìn)行設(shè)置
//設(shè)置事務(wù)的隔離級別
@Transactional(isolation = Isolation.DEFAULT)
@RequestMapping("/r4")
public String r4(String name, String password) throws IOException {
Integer reuslt = userService.insert(name, password);
if (true) {
throw new IOException();
}
return "注冊成功";
}4. 事務(wù)傳播機制
事務(wù)傳播機制是指在多個事務(wù)方法相互調(diào)用時,定義事務(wù)如何在這些方法之間傳播的規(guī)則,也就是延用調(diào)用方法的事務(wù)還是再重新開啟一個新事務(wù)

Spring 事務(wù)的傳播機制有以下七種
事務(wù)傳播機制 | 描述 | 理解(有 A,B 兩個方法,A 調(diào)用 B 對于 B 來說) |
Propagation.REQUIRED | 默認(rèn)的事務(wù)傳播級別。如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。 | A 有事務(wù)就用 A 的,沒有 B 就再開啟新的 |
Propagation.SUPPORTS | 如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。 | A 沒有事務(wù)就算了,B 就按照沒有事務(wù)的方式執(zhí)行 |
Propagation.MANDATORY | 如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則拋出異常。 | 如果 A 沒有事務(wù),就拋出異常 |
Propagation.REQUIRES_NEW | 如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。也就是說不管外部方法是否開啟事務(wù),Propagation.REQUIRES_NEW 修飾的內(nèi)部方法都會新開啟自己的事務(wù),且開啟的事務(wù)相互獨立,互不干擾。 | 不管 A 有沒有事務(wù),B 都要開啟新事務(wù) |
Propagation.NOT_SUPPORTED | 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起(不用)。 | 不管 A 有沒有事務(wù),B 都以非事務(wù)方式執(zhí)行 |
Propagation.NEVER | 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常。 | B 以非事務(wù)方式執(zhí)行,如果 A 有事務(wù)就拋出異常 |
Propagation.NESTED | 如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行。如果當(dāng)前沒有事務(wù),則該取值等價于 PROPAGATION_REQUIRED。 | 如果 A 有事務(wù),B 就創(chuàng)建一個嵌套事務(wù),如果沒有就創(chuàng)建新的 |
4.1. REQUIRED
把 UserService 和 LogService 的兩個方法都設(shè)置為 REQUIRED

@Slf4j
@RestController
@RequestMapping("/propaga")
public class PropagationController {
@Autowired
private UserService userService;
@Autowired
private LogService logService;
@Transactional
@RequestMapping("/r1")
public String registy(String name, String password) {
userService.insert(name, password);
logService.insertLog(name,"用戶注冊");
return "注冊成功";
}
}在 PropagationController 中進(jìn)行調(diào)用,此時 registy 就相當(dāng)于 A ,調(diào)用的兩個方法相當(dāng)于 B,運行之后,如果其中一個方法發(fā)生異常,那么 registy 方法的整個事務(wù)都會回滾,也就是他們都用的是 A 的事務(wù)

4.2. REQUIRES_NEW
把 UserService 和 LogService 的兩個方法都設(shè)置為 REQUIRES_NEW


此時就是無論 A 有沒有事務(wù), B 都新創(chuàng)建事務(wù),所以當(dāng) B 的一個方法有異常時,是不會影響其他方法的

4.3. NEVER
如果設(shè)置為 NEVER 的話,A 調(diào)用 B,A 如果存在事務(wù),就會報錯

把 A 的事務(wù)取消掉就不會報錯了

4.4. NESTED
NESTED 是如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行,所以說 A 和 B 不是同一個事務(wù),那么當(dāng) B 的一個方法出現(xiàn)異常時進(jìn)行回滾,另一個 A 調(diào)用的方法是不受影響的,也印證了這兩個不是同一個事務(wù),確實是創(chuàng)建了一個嵌套事務(wù)

和 REQUIRED 不同的是,那里用的是同一個事務(wù),其中一個回滾,都要回滾,這里可以只是自己的事務(wù)進(jìn)行回滾,也就是實現(xiàn)局部回滾
到此這篇關(guān)于詳解Spring事務(wù)和事務(wù)傳播機制的文章就介紹到這了,更多相關(guān)Spring 事務(wù)和事務(wù)傳播機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實現(xiàn)給出分?jǐn)?shù)數(shù)組得到對應(yīng)名次數(shù)組的方法
這篇文章主要介紹了java實現(xiàn)給出分?jǐn)?shù)數(shù)組得到對應(yīng)名次數(shù)組的方法,涉及java針對數(shù)組的遍歷、排序及運算的相關(guān)技巧,需要的朋友可以參考下2015-07-07
IDEA2019.2.2配置Maven3.6.2打開出現(xiàn)Unable to import Maven project
這篇文章主要介紹了IDEA2019.2.2配置Maven3.6.2打開出現(xiàn)Unable to import Maven project,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
IDEA2020.1啟動SpringBoot項目出現(xiàn)java程序包:xxx不存在
這篇文章主要介紹了IDEA2020.1啟動SpringBoot項目出現(xiàn)java程序包:xxx不存在,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06

