新聞中心
?大家好,我是樹哥。

Spring 事務(wù)是復(fù)雜一致性業(yè)務(wù)必備的知識(shí)點(diǎn),掌握好 Spring 事務(wù)可以讓我們寫出更好地代碼。這篇文章我們將介紹 Spring 事務(wù)的誕生背景,從而讓我們可以更清晰地了解 Spring 事務(wù)存在的意義。
接著,我們會(huì)介紹如何快速使用 Spring 事務(wù)。接著,我們會(huì)介紹 Spring 事務(wù)的一些特性,從而幫助我們更好地使用 Spring 事務(wù)。最后,我們會(huì)總結(jié)一些 Spring 事務(wù)常見的問題,避免大家踩坑。
Spring 事務(wù) - 思維導(dǎo)圖
誕生背景
當(dāng)我們聊起事務(wù)的時(shí)候,我們需要明白「事務(wù)」這個(gè)詞代表著什么。
事務(wù)其實(shí)是一個(gè)并發(fā)控制單位,是用戶定義的一個(gè)操作序列,這些操作要么全部完成,要不全部不完成,是一個(gè)不可分割的工作單位。事務(wù)有 ACID 四個(gè)特性,即:
- Atomicity(原子性):事務(wù)中的所有操作,或者全部完成,或者全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。
- 一致性(Consistency):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性沒有被破壞。
- 事務(wù)隔離(Isolation):多個(gè)事務(wù)之間是獨(dú)立的,不相互影響的。
- 持久性(Durability):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。
而我們說的 Spring 事務(wù),其實(shí)是事務(wù)在 Spring 中的實(shí)現(xiàn)。
明白了什么是事務(wù)之后,我們來聊聊:為什么要有 Spring 事務(wù)?
為了解釋清楚這個(gè)問題,我們舉個(gè)簡(jiǎn)單的例子:銀行里樹哥要給小黑轉(zhuǎn) 1000 塊錢,這時(shí)候會(huì)有兩個(gè)必要的操作:
- 將樹哥的賬戶余額減少 1000 元。
- 將小黑的賬戶余額增加 1000 元。
這兩個(gè)操作,要么一起都完成,要么都不完成。如果其中某個(gè)成功,另外一個(gè)失敗,那么就會(huì)出現(xiàn)嚴(yán)重的問題。而我們要保證這個(gè)操作的原子性,就必須通過 Spring 事務(wù)來完成,這就是 Spring 事務(wù)存在的原因。
如果你深入了解過 MySQL 事務(wù),那么你應(yīng)該知道:MySQL 默認(rèn)情況下,對(duì)于所有的單條語(yǔ)句都作為一個(gè)單獨(dú)的事務(wù)來執(zhí)行。我們要使用 MySQL 事務(wù)的時(shí)候,可以通過手動(dòng)提交事務(wù)來控制事務(wù)范圍。Spring 事務(wù)的本質(zhì),其實(shí)就是通過 Spring AOP 切面技術(shù),在合適的地方開啟事務(wù),接著在合適的地方提交事務(wù)或回滾事務(wù),從而實(shí)現(xiàn)了業(yè)務(wù)編程層面的事務(wù)操作。
使用指南
Spring 事務(wù)支持兩種使用方式,分別是:聲明式事務(wù)(注解方式)、編程式事務(wù)(代碼方式)。一般來說,我們使用聲明式事務(wù)比較多,這里我們就演示聲明式事務(wù)的使用方法。
項(xiàng)目準(zhǔn)備
為了較好地進(jìn)行講解,我們需要搭建一個(gè)具備數(shù)據(jù)庫(kù) CURD 功能的項(xiàng)目,并創(chuàng)建 tablea 和 tableb 兩張表。
首先,創(chuàng)建 tablea 和 tableb 兩張表,兩張表都只有 id 和 name 兩列,建表語(yǔ)句如下圖所示。
CREATE TABLE `tablea` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
CREATE TABLE `tableb` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
接著,創(chuàng)建一個(gè) SpringBoot 項(xiàng)目,隨后加入 MyBatis 及 MySQL 的 POM 依賴。
mysql
mysql-connector-java
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
最后,我們創(chuàng)建對(duì)應(yīng)的 controller 接口、service 接口、mapper 接口,代碼如下所示。
創(chuàng)建 controller 接口:
@SpringBootApplication
@RestController
@RequestMapping("/api")
public class SpringTransactionController {
@Autowired
private TransactionServiceA transactionServiceA;
@RequestMapping("/spring-transaction")
public String testTransaction() {
transactionServiceA.methodA();
return "SUCCESS";
}
}
創(chuàng)建 TableService 接口。
public interface TableService {
void insertTableA(TableEntity tableEntity);
void insertTableB(TableEntity tableEntity);
}創(chuàng)建 Service 接口實(shí)現(xiàn)類 TransactionServiceA 類,在 methodA () 方法中先往 tablea 表格插入一條數(shù)據(jù),隨后會(huì)調(diào)用 TransactionServiceB 服務(wù)的 methodB () 方法。
@Service
public class TransactionServiceA {
@Autowired
private TableService tableService;
@Autowired
private TransactionServiceB transactionServiceB;
public void methodA(){
System.out.println("methodA");
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
}
創(chuàng)建 TransactionServiceB 類實(shí)現(xiàn),在 methodB () 方法中往 tableb 表格插入一條數(shù)據(jù)。
@Service
public class TransactionServiceB {
@Autowired
private TableService tableService;
public void methodB(){
System.out.println("methodB");
tableService.insertTableB(new TableEntity());
}
}
創(chuàng)建 Mapper 接口方法:
@Mapper
public interface TableMapper {
@Insert("INSERT INTO tablea(id, name) " +
"VALUES(#{id}, #{name})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertTableA(TableEntity tableEntity);
@Insert("INSERT INTO tableb(id, name) " +
"VALUES(#{id}, #{name})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertTableB(TableEntity tableEntity);
}
數(shù)據(jù)庫(kù)表對(duì)應(yīng)的 TableEntity:
@Data
public class TableEntity {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
public TableEntity() {
}
public TableEntity(String name) {
this.name = name;
}
}
最后,我們?cè)谂渲梦募信渲煤脭?shù)據(jù)庫(kù)地址:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis 配置
mybatis:
type-aliases-package: tech.shuyi.javacodechip.spring_transaction.model
configuration:
map-underscore-to-camel-case: true
最后,我們運(yùn)行 SpringBoot 項(xiàng)目。通過瀏覽器訪問地址:localhost:8080/api/spring-transaction,正常的話應(yīng)該是接口請(qǐng)求成功。
查看數(shù)據(jù)庫(kù)表,會(huì)看到 tablea 和 tableb 都插入了一條數(shù)據(jù)。
到這里,我們用于測(cè)試 Spring 事務(wù)的 Demo 就準(zhǔn)備完畢了!
快速入門
使用聲明式事務(wù)的方法很簡(jiǎn)單,其實(shí)就是在 Service 層對(duì)應(yīng)方法上配置 @Transaction 注解即可。
假設(shè)我們的業(yè)務(wù)需求是:往 tablea 和 tableb 插入的數(shù)據(jù),要么都完成,要么都不完成。
這時(shí)候,我們應(yīng)該怎么操作呢?
首先,我們需要在 TransactionServiceA 類的 methodA () 方法上配置 @Transaction? 注解,同時(shí)也在 TransactionServiceB 類的 methodB () 方法上配置 @Transaction 注解。修改之后的 TransactionServiceA 和 TransactionServiceB 代碼如下所示。
// TransactionServiceA
@Transactional
public void methodA(){
System.out.println("methodA");
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
// TransactionServiceB
@Transactional
public void methodB(){
System.out.println("methodB");
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
可以看到,我們?cè)?methodB () 中模擬了業(yè)務(wù)異常,我們看看是否 tablea 和 tableb 都沒有插入數(shù)據(jù)。
修改之后重新啟動(dòng)項(xiàng)目,此時(shí)我們繼續(xù)訪問地址:localhost:8080/api/spring-transaction,我們會(huì)發(fā)現(xiàn)執(zhí)行錯(cuò)誤,并且控制臺(tái)也報(bào)錯(cuò)了。
這時(shí)候我們查看數(shù)據(jù)庫(kù),會(huì)發(fā)現(xiàn) tablea 和 tableb 都沒有插入數(shù)據(jù)。這說明事務(wù)起作用了。
事務(wù)傳播類型
事務(wù)傳播類型,指的是事務(wù)與事務(wù)之間的交互策略。例如:在事務(wù)方法 A 中調(diào)用事務(wù)方法 B,當(dāng)事務(wù)方法 B 失敗回滾時(shí),事務(wù)方法 A 應(yīng)該如何操作?這就是事務(wù)傳播類型。Spring 事務(wù)中定義了 7 種事務(wù)傳播類型,分別是:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。其中最常用的只有 3 種,即:REQUIRED、REQUIRES_NEW、NESTED。
針對(duì)事務(wù)傳播類型,我們要弄明白的是 4 個(gè)點(diǎn):
- 子事務(wù)與父事務(wù)的關(guān)系,是否會(huì)啟動(dòng)一個(gè)新的事務(wù)?
- 子事務(wù)異常時(shí),父事務(wù)是否會(huì)回滾?
- 父事務(wù)異常時(shí),子事務(wù)是否會(huì)回滾?
- 父事務(wù)捕捉異常后,父事務(wù)是否還會(huì)回滾?
REQUIRED
REQUIRED 是 Spring 默認(rèn)的事務(wù)傳播類型,該傳播類型的特點(diǎn)是:當(dāng)前方法存在事務(wù)時(shí),子方法加入該事務(wù)。此時(shí)父子方法共用一個(gè)事務(wù),無論父子方法哪個(gè)發(fā)生異?;貪L,整個(gè)事務(wù)都回滾。即使父方法捕捉了異常,也是會(huì)回滾。而當(dāng)前方法不存在事務(wù)時(shí),子方法新建一個(gè)事務(wù)。 為了驗(yàn)證 REQUIRED 事務(wù)傳播類型的特點(diǎn),我們來做幾個(gè)測(cè)試。
還是上面 methodA 和 methodB 的例子。當(dāng) methodA 不開啟事務(wù),methodB 開啟事務(wù),這時(shí)候 methodB 就是獨(dú)立的事務(wù),而 methodA 并不在事務(wù)之中。因此當(dāng) methodB 發(fā)生異?;貪L時(shí),methodA 中的內(nèi)容就不會(huì)被回滾。用如下的代碼就可以驗(yàn)證我們所說的。
public void methodA(){
System.out.println("methodA");
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
@Transactional
public void methodB(){
System.out.println("methodB");
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}最終的結(jié)果是:tablea 插入了數(shù)據(jù),tableb 沒有插入數(shù)據(jù),符合了我們的猜想。
當(dāng) methodA 開啟事務(wù),methodB 也開啟事務(wù)。按照我們的結(jié)論,此時(shí) methodB 會(huì)加入 methodA 的事務(wù)。此時(shí),我們驗(yàn)證當(dāng)父子事務(wù)分別回滾時(shí),另外一個(gè)事務(wù)是否會(huì)回滾。
我們先驗(yàn)證第一個(gè):當(dāng)父方法事務(wù)回滾時(shí),子方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA(){
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
throw new RuntimeException();
}
@Transactional
public void methodB(){
tableService.insertTableB(new TableEntity());
}
結(jié)果是:talbea 和 tableb 都沒有插入數(shù)據(jù),即:父事務(wù)回滾時(shí),子事務(wù)也回滾了。
我們繼續(xù)驗(yàn)證第二個(gè):當(dāng)子方法事務(wù)回滾時(shí),父方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA(){
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
@Transactional
public void methodB(){
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
結(jié)果是:talbea 和 tableb 都沒有插入數(shù)據(jù),即:子事務(wù)回滾時(shí),父事務(wù)也回滾了。
我們繼續(xù)驗(yàn)證第三個(gè):當(dāng)字方法事務(wù)回滾時(shí),父方法捕捉了異常,父方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA() {
tableService.insertTableA(new TableEntity());
try {
transactionServiceB.methodB();
} catch (Exception e) {
System.out.println("methodb occur exp.");
}
}
@Transactional
public void methodB() {
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
結(jié)果是:talbea 和 tableb 都沒有插入數(shù)據(jù),即:子事務(wù)回滾時(shí),父事務(wù)也回滾了。所以說,這也進(jìn)一步驗(yàn)證了我們之前所說的:REQUIRED 傳播類型,它是父子方法共用同一個(gè)事務(wù)的。
REQUIRES_NEW
REQUIRES_NEW 也是常用的一個(gè)傳播類型,該傳播類型的特點(diǎn)是:無論當(dāng)前方法是否存在事務(wù),子方法都新建一個(gè)事務(wù)。此時(shí)父子方法的事務(wù)時(shí)獨(dú)立的,它們都不會(huì)相互影響。但父方法需要注意子方法拋出的異常,避免因子方法拋出異常,而導(dǎo)致父方法回滾。 為了驗(yàn)證 REQUIRES_NEW 事務(wù)傳播類型的特點(diǎn),我們來做幾個(gè)測(cè)試。
首先,我們來驗(yàn)證一下:當(dāng)父方法事務(wù)發(fā)生異常時(shí),子方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA(){
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB(){
tableService.insertTableB(new TableEntity());
}
結(jié)果是:tablea 沒有插入數(shù)據(jù),tableb 插入了數(shù)據(jù),即:父方法事務(wù)回滾了,但子方法事務(wù)沒回滾。這可以證明父子方法的事務(wù)是獨(dú)立的,不相互影響。
下面,我們來看看:當(dāng)子方法事務(wù)發(fā)生異常時(shí),父方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA(){
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB(){
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
結(jié)果是:tablea 沒有插入了數(shù)據(jù),tableb 沒有插入數(shù)據(jù)。
從這個(gè)結(jié)果來看,貌似是子方法事務(wù)回滾,導(dǎo)致父方法事務(wù)也回滾了。但我們不是說父子事務(wù)都是獨(dú)立的,不會(huì)相互影響么?怎么結(jié)果與此相反呢?
其實(shí)是因?yàn)樽臃椒⊕伋隽水惓#阜椒ú]有做異常捕捉,此時(shí)父方法同時(shí)也拋出異常了,于是 Spring 就會(huì)將父方法事務(wù)也回滾了。如果我們?cè)诟阜椒ㄖ胁蹲疆惓?,那么父方法的事?wù)就不會(huì)回滾了,修改之后的代碼如下所示。
@Transactional
public void methodA(){
tableService.insertTableA(new TableEntity());
// 捕捉異常
try {
transactionServiceB.methodB();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB(){
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
結(jié)果是:tablea 插入了數(shù)據(jù),tableb 沒有插入數(shù)據(jù)。這正符合我們剛剛所說的:父子事務(wù)是獨(dú)立的,并不會(huì)相互影響。
這其實(shí)就是我們上面所說的:父方法需要注意子方法拋出的異常,避免因子方法拋出異常,而導(dǎo)致父方法回滾。因?yàn)槿绻麍?zhí)行過程中發(fā)生 RuntimeException 異常和 Error 的話,那么 Spring 事務(wù)是會(huì)自動(dòng)回滾的。
NESTED
NESTED 也是常用的一個(gè)傳播類型,該方法的特性與 REQUIRED 非常相似,其特性是:當(dāng)前方法存在事務(wù)時(shí),子方法加入在嵌套事務(wù)執(zhí)行。當(dāng)父方法事務(wù)回滾時(shí),子方法事務(wù)也跟著回滾。當(dāng)子方法事務(wù)發(fā)送回滾時(shí),父事務(wù)是否回滾取決于是否捕捉了異常。如果捕捉了異常,那么就不回滾,否則回滾。
可以看到 NESTED 與 REQUIRED 的區(qū)別在于:父方法與子方法對(duì)于共用事務(wù)的描述是不一樣的,REQUIRED 說的是共用同一個(gè)事務(wù),而 NESTED 說的是在嵌套事務(wù)執(zhí)行。這一個(gè)區(qū)別的具體體現(xiàn)是:在子方法事務(wù)發(fā)生異常回滾時(shí),父方法有著不同的反應(yīng)動(dòng)作。
對(duì)于 REQUIRED 來說,無論父子方法哪個(gè)發(fā)生異常,全都會(huì)回滾。而 REQUIRED 則是:父方法發(fā)生異?;貪L時(shí),子方法事務(wù)會(huì)回滾。而子方法事務(wù)發(fā)送回滾時(shí),父事務(wù)是否回滾取決于是否捕捉了異常。
為了驗(yàn)證 NESTED 事務(wù)傳播類型的特點(diǎn),我們來做幾個(gè)測(cè)試。
首先,我們來驗(yàn)證一下:當(dāng)父方法事務(wù)發(fā)生異常時(shí),子方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA() {
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
tableService.insertTableB(new TableEntity());
}
結(jié)果是:tablea 和 tableb 都沒有插入數(shù)據(jù),即:父子方法事務(wù)都回滾了。這說明父方法發(fā)送異常時(shí),子方法事務(wù)會(huì)回滾。
接著,我們繼續(xù)驗(yàn)證一下:當(dāng)子方法事務(wù)發(fā)生異常時(shí),如果父方法沒有捕捉異常,父方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA() {
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
結(jié)果是:tablea 和 tableb 都沒有插入數(shù)據(jù),即:父子方法事務(wù)都回滾了。這說明子方法發(fā)送異?;貪L時(shí),如果父方法沒有捕捉異常,那么父方法事務(wù)也會(huì)回滾。
最后,我們驗(yàn)證一下:當(dāng)子方法事務(wù)發(fā)生異常時(shí),如果父方法捕捉了異常,父方法事務(wù)是否會(huì)回滾?
@Transactional
public void methodA() {
tableService.insertTableA(new TableEntity());
try {
transactionServiceB.methodB();
} catch (Exception e) {
}
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
tableService.insertTableB(new TableEntity());
throw new RuntimeException();
}
結(jié)果是:tablea 插入了數(shù)據(jù),tableb 沒有插入數(shù)據(jù),即:父方法事務(wù)沒有回滾,子方法事務(wù)回滾了。這說明子方法發(fā)送異常回滾時(shí),如果父方法捕捉了異常,那么父方法事務(wù)就不會(huì)回滾。
看到這里,相信大家已經(jīng)對(duì) REQUIRED、REQUIRES_NEW 和 NESTED 這三個(gè)傳播類型有了深入的理解了。最后,讓我們來總結(jié)一下:
|
事務(wù)傳播類型 |
特性 |
|
REQUIRED |
當(dāng)前方法存在事務(wù)時(shí),子方法加入該事務(wù)。此時(shí)父子方法共用一個(gè)事務(wù),無論父子方法哪個(gè)發(fā)生異?;貪L,整個(gè)事務(wù)都回滾。即使父方法捕捉了異常,也是會(huì)回滾。而當(dāng)前方法不存在事務(wù)時(shí),子方法新建一個(gè)事務(wù)。 |
|
REQUIRES_NEW |
無論當(dāng)前方法是否存在事務(wù),子方法都新建一個(gè)事務(wù)。此時(shí)父子方法的事務(wù)時(shí)獨(dú)立的,它們都不會(huì)相互影響。但父方法需要注意子方法拋出的異常,避免因子方法拋出異常,而導(dǎo)致父方法回滾。 |
|
NESTED |
當(dāng)前方法存在事務(wù)時(shí),子方法加入在嵌套事務(wù)執(zhí)行。當(dāng)父方法事務(wù)回滾時(shí),子方法事務(wù)也跟著回滾。當(dāng)子方法事務(wù)發(fā)送回滾時(shí),父事務(wù)是否回滾取決于是否捕捉了異常。如果捕捉了異常,那么就不回滾,否則回滾。 |
應(yīng)該怎么用?
看完了事務(wù)的傳播類型,我們對(duì) Spring 事務(wù)又有了深刻的理解。
看到這里,你應(yīng)該也明白:使用事務(wù),不再是簡(jiǎn)單地使用 @Transaction 注解就可以,還需要根據(jù)業(yè)務(wù)場(chǎng)景,選擇合適的傳播類型。那么我們?cè)偕A一下使用 Spring 事務(wù)的方法論。一般來說,使用 Spring 事務(wù)的步驟為:
根據(jù)業(yè)務(wù)場(chǎng)景,分析要達(dá)成的事務(wù)效果,確定使用的事務(wù)傳播類型。
在 Service 層使用 @Transaction 注解,配置對(duì)應(yīng)的 propogation 屬性。
下次遇到要使用事務(wù)的情況,記得按照這樣的步驟去做哦~
Spring 事務(wù)失效
什么時(shí)候 Spring 事務(wù)會(huì)失效?
若同一類中的其他沒有 @Transactional 注解的方法內(nèi)部調(diào)用有 @Transactional 注解的方法,有 @Transactional 注解的方法的事務(wù)會(huì)失效。
這是由于 Spring AOP 代理的原因造成的,因?yàn)橹挥挟?dāng) @Transactional 注解的方法在類以外被調(diào)用的時(shí)候,Spring 事務(wù)管理才生效。
另外,如果直接調(diào)用,不通過對(duì)象調(diào)用,也是會(huì)失效的。因?yàn)?Spring 事務(wù)是通過 AOP 實(shí)現(xiàn)的。
@Transactional 注解只有作用到 public 方法上事務(wù)才生效。
被 @Transactional 注解的方法所在的類必須被 Spring 管理。
底層使用的數(shù)據(jù)庫(kù)必須支持事務(wù)機(jī)制,否則不生效。
彩蛋
Spring 事務(wù)執(zhí)行過程中,如果拋出非 RuntimeException 和非 Error 錯(cuò)誤的其他異常,那么是不會(huì)回滾的哦。例如下面的代碼執(zhí)行后,tablea 和 tableb 兩個(gè)表格,都會(huì)插入一條數(shù)據(jù)。
@Transactional
public void methodA() throws Exception {
tableService.insertTableA(new TableEntity());
transactionServiceB.methodB();
}
@Transactional
public void methodB() throws Exception {
tableService.insertTableB(new TableEntity());
// 非 RuntimeException
throw new Exception();
}
參考資料
- 咱們從頭到尾說一次 Spring 事務(wù)管理(器) - SegmentFault 思否
- 【技術(shù)干貨】Spring 事務(wù)原理一探 - 知乎
- Spring 事務(wù)詳解 | JavaGuide
- 事務(wù)之六:spring 嵌套事務(wù) - duanxz - 博客園
- 例子很詳細(xì),不錯(cuò)!VIP!NESTED 區(qū)別!spring 事務(wù)傳播行為詳解 - 雙間 - 博客園
- Spring Boot 實(shí)戰(zhàn) —— MyBatis(注解版)使用方法 | Michael 翔
- 記一次事務(wù)的坑 Transaction rolled back because it has been marked as rollback-only - 云揚(yáng)四海?
網(wǎng)站名稱:深入理解Spring事務(wù):入門、使用、原理
文章URL:http://fisionsoft.com.cn/article/cddhcsp.html


咨詢
建站咨詢
