新聞網(wǎng)站建設源碼今日國內(nèi)新聞大事20條
目錄
1.事務回顧
1.1 什么是事務
1.2 為什么需要事務
1.3 事務的操作
2. Spring 中事務的實現(xiàn)
2.1 Spring 編程式事務(了解)
2.2 Spring聲明式事務 @Transactional
對比事務提交和回滾的日志
3. @Transactional詳解
3.1 rollbackFor
3.2 @Transactional 注解什么時候會失效
3.3 事務的隔離級別
3.3.1 MySQL 事務隔離級別
3.3.2 Spring 事務隔離級別
3.4 Spring 事務傳播機制
3.4.1 什么是事務傳播機制
3.4.2 事務的傳播機制有哪些
1.事務回顧
1.1 什么是事務
事務是一組操作的集合, 是一個不可分割的操作:
事務會把所有的操作作為一個整體, 一起向數(shù)據(jù)庫提交或者是撤銷操作請求, 所以這組操作要么同時成功, 要么同時失敗.
1.2 為什么需要事務
我們在進行程序開發(fā)時,也會有事務的需求
比如轉(zhuǎn)賬操作:
第一步: A賬戶 -100 元.
第二步: B 賬戶 +100 元
如果沒有事務,第一步執(zhí)行成功了, 第二步執(zhí)行失敗了, 那么A 賬戶的100 元就平白無故消失了.? ? 如果使用事務就可以解決這個問題, 讓這一組操作要么一起成功, 要么一起失敗.
比如秒殺系統(tǒng),
第一步: 下單成功
第二步:? 扣減庫存
下單成功后, 庫存也需要同步減少, 如果下單成功, 庫存扣減失敗, 那么就會造成下單超出的情況.? 所以就需要把這兩步操作放在同一個事務中. 要么一起成功, 要么一起失敗.
理解事務概念為主, 實際企業(yè)開發(fā)時, 并不是簡單的通過事務來處理。
1.3 事務的操作
事務的操作主要有三步:?
-- 開啟事務
start transaction;-- 提交事務
commit;-- 回滾事務
rollback;
2. Spring 中事務的實現(xiàn)
Spring 中的事務操作分為兩類:
1.編程式事務 (手動寫代碼操作事務)
2.聲明式事務 (利用注解自動開啟和提交事務)
在學習事務之前, 我們先準備數(shù)據(jù)和數(shù)據(jù)的訪問代碼
需求: 用戶注冊, 注冊時在日志表中插入一條操作記錄.
數(shù)據(jù)準備:
-- 創(chuàng)建數(shù)據(jù)庫
DROP DATABASE IF EXISTS trans_test;
CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;use trans_test;
-- ??表
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (`id` INT NOT NULL AUTO_INCREMENT,`user_name` VARCHAR (128) NOT NULL,`password` VARCHAR (128) NOT NULL,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '??表';-- 操作?志表
DROP TABLE IF EXISTS log_info;
CREATE TABLE log_info (`id` INT PRIMARY KEY auto_increment,`user_name` VARCHAR ( 128 ) NOT NULL,`op` VARCHAR ( 256 ) NOT NULL,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now()
) DEFAULT charset 'utf8mb4';
代碼準備:
model層:
mapper層:?
server層:?
2.1 Spring 編程式事務(了解)
Spring 手動操作事務和上面 MySOL操作事務類似,有3個重要操作步驟:
SpringBoot 內(nèi)置了兩個對象:
我們還是根據(jù)代碼的實現(xiàn)來學習:
package com.example.trans.controller;import com.example.trans.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserService userService;@RequestMapping("register")public Boolean register(String username, String password) {//開啟事務TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);Integer result = userService.insert(username, password);System.out.println("插入用戶表, result: " + result);//回滾事務
// dataSourceTransactionManager.rollback(transaction);//提交事務dataSourceTransactionManager.commit(transaction);return true;}
}
?這部分代碼了解即可:
以上代碼雖然可以實現(xiàn)事務,但操作也很繁瑣,有沒有更簡單的實現(xiàn)方法呢?
接下來我們學習聲明式事務
2.2 Spring聲明式事務 @Transactional
聲明式事務的實現(xiàn)很簡單
在需要事務的方法上添加 @Transactional
注解就可以實現(xiàn)了.無需手動開啟事務和提交事務, 進入方法時自動開啟事務, 方法執(zhí)行完會自動提交事務, 如果中途發(fā)生了沒有處理的異常會自動回滾事務.
我們來看代碼實現(xiàn):
@RequestMapping("/trans")
@RestController
public class TransController {@AutowiredUserService userService;@AutowiredLogService logService;@Transactional@RequestMapping("/register")public Boolean register(String userName, String password) {Integer result = userService.insert(userName, password);System.out.println("插入用戶表, result: " + result);return true;}
}
運行程序, 發(fā)現(xiàn)數(shù)據(jù)插入成功
修改程序,使之出現(xiàn)異常
運行程序:
發(fā)現(xiàn)雖然日志顯示數(shù)據(jù)插入成功, 但數(shù)據(jù)庫卻沒有新增數(shù)據(jù), 事務進行了回滾
我們一般會在業(yè)務邏輯層當中來控制事務, 因為在業(yè)務邏輯層當中, 一個業(yè)務功能可能會包含多個數(shù)據(jù)訪問的操作, 在業(yè)務邏輯層來控制事務, 我們就可以將多個數(shù)據(jù)訪問操作控制在一個事務范圍內(nèi)上述代碼在Controller中書寫,? 只是為了方便學習.
@Transactional作用
@Transactional 可以用來修飾方法或類:
方法/類 被 @Transactional 注解修飾時, 在目標方法執(zhí)行開始之前, 會自動開啟事務, 方法執(zhí)行結(jié)束
之后,自動提交事務.
如果在方法執(zhí)行過程中, 出現(xiàn)異常, 且異常未被捕獲, 就進行事務回滾操作
如果異常被程序捕獲, 方法就被認為是成功執(zhí)行, 依然會提交事務,
運行程序, 事務成功提交,:?
在上述程序中, 發(fā)現(xiàn)雖然程序出錯了, 但是由于異常被捕獲了, 所以事務依然得到了提交
如果需要事務進行回滾, 有以下兩種方式:
1.重新拋出異常
2.手動回滾事務
對比事務提交和回滾的日志
事務提交時:
事務回滾時:
當事務提交時, 日志會含有:?Transaction synchronization committing SqlSession
3. @Transactional詳解
通過上面的代碼, 我們學習了 @Transactional 的基本使用. 接下來我們學習 @Transactional
注解的使用細節(jié).
我們主要學習 @Transactional 注解當中的三個常見屬性:
1. rollbackFor: 異?;貪L屬性, 指定能夠觸發(fā)事務回滾的異常類型, 可以指定多個異常類型
2.lsolation: 事務的隔離級別. 默認值為 Isolation.DEFAULT
3. propagation: 事務的傳播機制. 默認值為 Propagation.REQUIRED
3.1 rollbackFor
- 默認情況下,Spring 的事務管理器只會在拋出運行時異常(
RuntimeException及其子類
)和錯誤(Error
)時才會回滾事務。這是因為在 Java 的異常體系中,非運行時異常(例如IOException
、SQLException
等檢查異常)通常被認為是可以預期和合理處理的情況。 - 例如,如果你有一個方法是從文件中讀取數(shù)據(jù),可能會拋出
IOException
。在這種情況下,開發(fā)人員可能希望在捕獲這個異常后進行一些特定的處理,比如記錄日志、提示用戶重新輸入等操作,而不是直接回滾事務。
驗證拋出IOException異常, 事務提交:
?運行程序, 事務成功提交:
如果我們需要所有異常都回滾, 需要來配置 @Transactional 注解當中的 rollbackFor 屬性, 通
過 rollbackFor 這個屬性指定出現(xiàn)何種異常類型時事務進行回滾.
結(jié)論:
1.在Spring的事務管理中,默認只在遇到運行時異常 RuntimeException 和 Error 時才會回滾
2.如果需要回滾指定類型的異常, 可以通過 rollbackFor 屬性來指定?
3.2 @Transactional 注解什么時候會失效
1. 方法的訪問權(quán)限問題
情況說明:如果@Transactional
注解標記的方法是private
的,那么這個注解將會失效。這是因為 Spring 事務管理是基于代理的機制來實現(xiàn)的,代理對象無法訪問目標對象的private
方法。
示例代碼:
import org.springframework.transaction.annotation.Transactional;
public class TransactionalService {@Transactionalprivate void privateTransactionalMethod() {// 業(yè)務邏輯}
}
解釋:在這個示例中,privateTransactionalMethod
方法雖然被標注了@Transactional
,但是由于它是private
方法,Spring 無法為其創(chuàng)建有效的代理來管理事務,所以該注解實際上不會起作用。
2. 方法內(nèi)部調(diào)用自身方法的情況
情況說明:當一個類中的方法 A 調(diào)用了同一個類中的另一個被@Transactional
注解標記的方法 B,并且這個調(diào)用是在方法 A 內(nèi)部直接調(diào)用(而不是通過代理對象調(diào)用)時,方法 B 上的@Transactional
注解會失效。這是因為在這種情況下,沒有經(jīng)過 Spring 的代理攔截,也就無法開啟事務管理。
示例代碼:
import org.springframework.transaction.annotation.Transactional;
public class TransactionalService {public void outerMethod() {innerTransactionalMethod();}@Transactionalpublic void innerTransactionalMethod() {// 業(yè)務邏輯}
}
解釋:在這個例子中,outerMethod
直接調(diào)用innerTransactionalMethod
,這種內(nèi)部調(diào)用不會觸發(fā) Spring 的事務代理,所以innerTransactionalMethod
上的@Transactional
注解在這里不會產(chǎn)生事務管理的效果。
3. 異常被捕獲但沒有重新拋出的情況
情況說明:@Transactional
注解默認是在運行時異常(RuntimeException
)和錯誤(Error
)拋出時才會回滾事務。如果在被@Transactional
注解標記的方法中捕獲了異常,并且沒有重新拋出(對于需要回滾事務的異常類型),那么事務將不會回滾。
示例代碼:
import org.springframework.transaction.annotation.Transactional;
public class TransactionalService {@Transactionalpublic void methodWithTransaction() {try {// 業(yè)務邏輯,可能會拋出RuntimeExceptionthrow new RuntimeException("模擬異常");} catch (RuntimeException e) {// 捕獲異常但沒有重新拋出}}
}
解釋:在這個示例中,methodWithTransaction
方法中雖然拋出了RuntimeException
,但是在捕獲這個異常后沒有重新拋出,所以事務不會回滾。
4.數(shù)據(jù)庫不支持事務或者事務配置錯誤的情況
情況說明:如果使用的數(shù)據(jù)庫本身不支持事務(雖然這種情況比較少見,大多數(shù)主流數(shù)據(jù)庫都支持事務),或者在 Spring 配置中事務相關(guān)的配置(如數(shù)據(jù)源、事務管理器等)出現(xiàn)錯誤,那么@Transactional
注解也會失效。
5. 使用了不兼容的事務傳播行為組合
情況說明:在多個方法嵌套調(diào)用并且每個方法都有@Transactional
注解的情況下,如果事務傳播行為組合不兼容,可能會導致事務失效或者不符合預期。例如,在一個方法中使用REQUIRES_NEW
傳播行為,而在嵌套調(diào)用的方法中使用NEVER
傳播行為,這可能會導致事務無法正常開啟或者出現(xiàn)沖突。
示例代碼:
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class TransactionalService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void outerMethod() {innerMethod();}@Transactional(propagation = Propagation.NEVER)public void innerMethod() {// 業(yè)務邏輯}
}
解釋:在這個例子中,outerMethod
要求開啟一個新事務(REQUIRES_NEW
),而innerMethod
卻聲明不允許在事務中執(zhí)行(NEVER
),這種不兼容的傳播行為組合可能會導致事務管理出現(xiàn)問題,使得@Transactional
注解無法按照預期工作。
3.3 事務的隔離級別
3.3.1 MySQL 事務隔離級別
SQL標準定義了四種隔離級別, MySQL全都支持, 這四種隔離級別分別是:
1.讀未提交(READ UNCOMMITTED): 讀未提交, 也叫未提交讀, 該隔離級別的事務可以看到其他事務中未提交的數(shù)據(jù).
因為其他事務未提交的數(shù)據(jù)可能會發(fā)生回滾,但是該隔離級別卻可以讀到,我們把該級別讀到的數(shù)據(jù)稱之為臟數(shù)據(jù),這個問題稱之為臟讀。
2.讀提交(READ COMMITTED): 讀已提交, 也叫提交讀, 該隔離級別的事務能讀取到已經(jīng)提交事務的數(shù)據(jù).
該隔離級別不會有臟讀的問題, 但由于在事務的執(zhí)行中可以讀取到其他事務提交的結(jié)果, 所以在不同時間的相同 SOL 查詢可能會得到不同的結(jié)果, 這種現(xiàn)象叫做不可重復讀
3. 可重復讀(REPEATABLE READ): 事務不會讀到其他事務對已有數(shù)據(jù)的修改, 即使其他事務已提交. 也就可以確保同一事務多次查詢的結(jié)果一致, 但是其他事務新插入的數(shù)據(jù), 是可以感知到的. 這也就引發(fā)了幻讀問題. 可重復讀, 是 MySQL 的默認事務隔離級別.
比如此級別的事務正在執(zhí)行時,另一個事務成功的插入了某條數(shù)據(jù),但因為它每次查詢的結(jié)果都是一樣的,所以會導致查詢不到這條數(shù)據(jù),自己重復插入時又失敗(因為唯一約束的原因).明明在事務中查詢不到這條信息,但自己就是插入不進去,這個現(xiàn)象叫幻讀.
4.串行化(SERIALIZABLE): 序列化, 事務最高隔離級別, 它會強制事務排序, 使之不會發(fā)生沖突, 從而解決了臟讀, 不可重復讀和幻讀問題, 但因為執(zhí)行效率低,所以真正使用的場景并不多,
讀未提交(Read Uncommitted)
- 定義:這是最低的隔離級別。在這個級別下,一個事務可以讀取到另一個事務未提交的數(shù)據(jù)。這就可能導致臟讀(Dirty Read)的問題。
- 臟讀示例:假設有兩個事務,事務 A 和事務 B。事務 A 修改了一條記錄但尚未提交,事務 B 此時讀取了這條被修改的記錄。如果事務 A 后來回滾了修改,那么事務 B 讀取到的數(shù)據(jù)就是 “臟” 數(shù)據(jù),這種情況就是臟讀。例如,事務 A 將賬戶余額從 100 元修改為 200 元,但還沒提交,事務 B 讀取賬戶余額為 200 元,然后事務 A 回滾,實際余額還是 100 元,事務 B 讀取的數(shù)據(jù)就不準確了。
- 應用場景:這種隔離級別一般很少在實際生產(chǎn)環(huán)境中使用,因為臟讀問題可能會導致數(shù)據(jù)的不一致性。不過在一些對數(shù)據(jù)準確性要求不高,并且需要最大程度提高性能和并發(fā)度的場景下,可能會考慮使用,比如某些數(shù)據(jù)分析系統(tǒng),對數(shù)據(jù)的實時性要求高于準確性。
讀已提交(Read Committed)
- 定義:在這個隔離級別下,一個事務只能讀取另一個事務已經(jīng)提交的數(shù)據(jù),避免了臟讀的問題。但是可能會出現(xiàn)不可重復讀(Non - Repeatable Read)的情況。
- 不可重復讀示例:事務 A 在執(zhí)行過程中,兩次讀取同一條記錄,在兩次讀取之間,事務 B 修改并提交了這條記錄。這樣事務 A 兩次讀取的結(jié)果就不一樣了。例如,事務 A 先讀取賬戶余額為 100 元,然后事務 B 將余額修改為 200 元并提交,事務 A 再次讀取余額就變成了 200 元,這就是不可重復讀。
- 應用場景:在很多對數(shù)據(jù)準確性有一定要求的業(yè)務場景中比較常用,比如一般的 Web 應用程序中的查詢操作,大多數(shù)情況下可以接受不可重復讀的情況,因為這些查詢通常是獲取最新的數(shù)據(jù)狀態(tài)。
可重復讀(Repeatable Read)
- 定義:在這個隔離級別下,一個事務在執(zhí)行過程中多次讀取同一數(shù)據(jù)會得到相同的結(jié)果,不會出現(xiàn)不可重復讀的情況。不過,在這個級別下可能會出現(xiàn)幻讀(Phantom Read)的情況。
- 幻讀示例:事務 A 在執(zhí)行過程中,根據(jù)某個條件查詢記錄,第一次查詢得到了一定數(shù)量的記錄。在事務 A 還未結(jié)束時,事務 B 插入了符合事務 A 查詢條件的新記錄并提交。當事務 A 再次根據(jù)相同條件查詢時,就會發(fā)現(xiàn)比第一次查詢多了一些記錄,就好像出現(xiàn)了 “幻影” 一樣。例如,事務 A 查詢年齡大于 30 歲的員工名單,第一次查詢有 5 個人,然后事務 B 插入了一個年齡大于 30 歲的新員工記錄并提交,事務 A 再次查詢就有 6 個人了。
- 應用場景:對于一些對數(shù)據(jù)一致性要求較高的業(yè)務場景,如金融系統(tǒng)中的賬戶交易記錄查詢等,可重復讀是比較合適的隔離級別。它可以保證在一個事務內(nèi)部,對同一數(shù)據(jù)的讀取是穩(wěn)定的。
串行化(Serializable)
- 定義:這是最高的隔離級別。在這個級別下,事務是串行執(zhí)行的,一個事務必須等待前一個事務完成后才能開始,就像把多個事務的執(zhí)行順序排好隊一樣。這樣可以避免臟讀、不可重復讀和幻讀的問題。
- 應用場景:在對數(shù)據(jù)準確性和一致性要求極高的場景下使用,如一些涉及到高價值交易或者重要數(shù)據(jù)修改的系統(tǒng),像銀行的核心賬務系統(tǒng)等。不過,這種隔離級別會嚴重影響系統(tǒng)的并發(fā)性能,因為事務是串行執(zhí)行的,所以在使用時需要權(quán)衡性能和數(shù)據(jù)一致性的需求。
3.3.2 Spring 事務隔離級別
Spring 中事務隔離級別有5 種:
package org.springframework.transaction.annotation;public enum Isolation {DEFAULT(-1),READ_UNCOMMITTED(1),READ_COMMITTED(2),REPEATABLE_READ(4),SERIALIZABLE(8);private final int value;private Isolation(int value) {this.value = value;}public int value() {return this.value;}
}
Spring 中事務隔離級別可以通過 @Transactional 中的 isolation 屬性進行設置
@Transactional(isolation = Isolation.READ_COMMITTED)
3.4 Spring 事務傳播機制
3.4.1 什么是事務傳播機制
事務傳播機制就是: 多個事務方法存在調(diào)用關(guān)系時, 事務是如何在這些方法間進行傳播的。
比如有兩個方法 A, B 都被 @Transactional 修飾,A方法調(diào)用B方法
A方法運行時, 會開啟一個事務. 當A調(diào)用B時, B方法本身也有事務, 此時B方法運行時, 是加入A的事務, 還是創(chuàng)建一個新的事務呢?
這個就涉及到了事務的傳播機制.
比如公司流程管理
執(zhí)行任務之前,需要先寫執(zhí)行文檔,任務執(zhí)行結(jié)束,再寫總結(jié)匯報
此時A部門有一項工作, 需要B部門的支援, 此時B部門是直接使用A部門的文檔, 還是新建一個文檔呢?
事務隔離級別解決的是多個事務同時調(diào)用一個數(shù)據(jù)庫的問題
而事務傳播機制解決的是一個事務在多個節(jié)點(方法)中傳遞的問題
3.4.2 事務的傳播機制有哪些
@Transactional 注解支持事務傳播機制的設置, 通過 propagation 屬性來指定傳播行為
Spring 事務傳播機制有以下7種:
public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);private final int value;private Propagation(int value) {this.value = value;}public int value() {return this.value;}
}
比如一對新人要結(jié)婚了, 關(guān)于是否需要房子
1.REQUIRED(默認)
- 定義:如果當前沒有事務,就創(chuàng)建一個新事務;如果當前已經(jīng)存在一個事務,就加入這個事務。這是最常用的傳播機制。
- 示例場景:
- 假設我們有一個業(yè)務方法 A 調(diào)用另一個業(yè)務方法 B,方法 A 已經(jīng)開啟了一個事務。當方法 B 使用
REQUIRED
傳播機制時,它會直接加入到方法 A 的事務中。例如,在一個電商系統(tǒng)中,下單操作(方法 A)開啟了一個事務,在下單過程中需要更新庫存(方法 B),方法 B 就可以使用REQUIRED
傳播機制加入到下單事務中,保證下單和庫存更新要么都成功,要么都失敗。- 代碼示例:
?@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.REQUIRED) public void methodB() {// 加入methodA的事務中 }
2.SUPPORTS
- 定義:如果當前存在事務,就加入當前事務;如果當前沒有事務,就以非事務方式執(zhí)行。
- 示例場景:
- 考慮一個日志記錄方法。如果在一個事務性的業(yè)務操作過程中調(diào)用它,它可以加入事務,保證日志記錄和業(yè)務操作一起成功或失敗;但如果是在非事務環(huán)境下調(diào)用,它也能正常工作,只是不會有事務管理。例如,在一個用戶注冊系統(tǒng)中,注冊方法(有事務)可能會調(diào)用記錄注冊日志的方法,日志方法使用
SUPPORTS
傳播機制,就可以在注冊事務中記錄日志;而單獨測試日志方法時,它也能正常記錄日志而不需要事務。- 代碼示例:
?@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.SUPPORTS) public void methodB() {// 如果methodA有事務就加入,沒有就非事務執(zhí)行 }
3.MANDATORY
- 定義:如果當前存在事務,就加入當前事務;如果當前沒有事務,就拋出異常。
- 示例場景:
- 想象一個必須在事務環(huán)境下執(zhí)行的關(guān)鍵業(yè)務操作,比如在一個資金轉(zhuǎn)賬系統(tǒng)中,有一個計算轉(zhuǎn)賬手續(xù)費的方法,這個方法必須在轉(zhuǎn)賬事務中執(zhí)行,因為手續(xù)費的計算和轉(zhuǎn)賬操作緊密相關(guān)。如果調(diào)用這個方法時沒有事務,就應該拋出異常,提醒開發(fā)人員必須在事務環(huán)境下調(diào)用。
- 代碼示例:
?@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.MANDATORY) public void methodB() {// 如果沒有事務就拋出異常,有事務就加入 }
4.REQUIRES_NEW
- 定義:不管當前是否存在事務,都創(chuàng)建一個新事務。如果當前已經(jīng)存在一個事務,就先暫停當前事務,等新事務執(zhí)行完畢后,再恢復原來的事務。
- 示例場景:
- 在一個電商系統(tǒng)中,下單操作(有事務)可能需要調(diào)用一個發(fā)送通知的方法。發(fā)送通知的操作應該獨立于下單事務,即使下單事務回滾,通知也已經(jīng)發(fā)送出去了。所以發(fā)送通知的方法可以使用
REQUIRES_NEW
傳播機制,開啟一個新事務來發(fā)送通知。- 代碼示例:
?@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() {// 開啟新事務,與methodA的事務獨立 }
5.NOT_SUPPORTED
- 定義:以非事務方式執(zhí)行操作,并且如果當前存在事務,就先暫停當前事務,等操作執(zhí)行完畢后,再恢復原來的事務。
- 示例場景:
- 假設在一個事務性的業(yè)務流程中,需要調(diào)用一個外部的統(tǒng)計服務。這個統(tǒng)計服務本身不需要事務管理,而且為了不影響原來事務的性能,最好以非事務方式執(zhí)行。例如,在一個訂單處理系統(tǒng)中,有一個事務性的訂單審核方法,在審核過程中需要調(diào)用一個外部的訂單統(tǒng)計服務,這個統(tǒng)計服務就可以使用
NOT_SUPPORTED
傳播機制,暫停審核事務,執(zhí)行完統(tǒng)計后再恢復審核事務。- 代碼示例:
?@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodB() {// 暫停methodA的事務,非事務執(zhí)行,然后恢復methodA的事務 }
6.NEVER
- 定義:以非事務方式執(zhí)行操作,如果當前存在事務,就拋出異常。
- 示例場景:
- 考慮一個簡單的查詢方法,它只是用于快速獲取數(shù)據(jù),不應該在事務環(huán)境下執(zhí)行。如果在一個有事務的上下文中調(diào)用這個查詢方法,就應該拋出異常,因為它可能會影響查詢性能或者不符合設計意圖。例如,在一個數(shù)據(jù)查詢接口中,有一個簡單的獲取用戶基本信息的方法,這個方法可以使用
NEVER
傳播機制,確保它不會在事務環(huán)境下執(zhí)行。- 代碼示例:
?@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.NEVER) public void methodB() {// 如果有事務就拋出異常,以非事務執(zhí)行 }
7.NESTED
- 定義:如果當前存在事務,就在當前事務中嵌套一個子事務。子事務可以獨立于父事務提交或回滾,但是如果父事務回滾,子事務也會回滾。
- 示例場景:
- 在一個復雜的業(yè)務流程中,可能有一個主業(yè)務操作和一些附屬的子業(yè)務操作。例如,在一個訂單處理系統(tǒng)中,主訂單處理事務(父事務)可能包含一個子事務,如更新相關(guān)的推薦商品信息。如果推薦商品信息更新成功(子事務成功),但主訂單處理失敗(父事務失敗),那么子事務也會回滾;而如果子事務失敗,父事務可以根據(jù)具體情況決定是否繼續(xù)執(zhí)行或者也回滾。
- 代碼示例:
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 事務開始methodB();// 事務提交或回滾 } @Transactional(propagation = Propagation.NESTED) public void methodB() {// 在methodA的事務中創(chuàng)建嵌套子事務 }
總結(jié)
?