Spring聲明式事務(wù)@Transactional的用法解讀
Spring 聲明式事務(wù) @Transactional的使用很簡(jiǎn)單,只需要添加依賴,在需要的方法或者類上添加 @Transactional注解即可。
一、添加依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency>
在需要事務(wù)的方法上添加 @Transactional 注解就可以實(shí)現(xiàn)了,無(wú)需手動(dòng)開啟事務(wù)和提交事務(wù),進(jìn)入方法時(shí)自動(dòng)開啟事務(wù),方法執(zhí)行完會(huì)自動(dòng)提交事務(wù),如果中途發(fā)生了沒有處理的異常會(huì)自動(dòng)回滾事務(wù)。
代碼示例:
@RequestMapping("/trans")
@RestController
public class TransactionalController {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/registry")
public String registry(String name,String password){
//用戶注冊(cè)
userService.registryUser(name,password);
return "注冊(cè)成功";
}
}假設(shè)代碼有異常:
@Slf4j
@RequestMapping("/trans")
@RestController
public class TransactionalController {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/registry")
public String registry(String name,String password){
//?戶注冊(cè)
userService.registryUser(name,password);
log.info("用戶數(shù)據(jù)插?成功");
//強(qiáng)制程序拋出異常
int a = 10/0;
return "注冊(cè)成功";
}
}
啟動(dòng)項(xiàng)目發(fā)現(xiàn)雖然日志顯示數(shù)據(jù)插入成功,但數(shù)據(jù)庫(kù)卻沒有新增數(shù)據(jù),事務(wù)進(jìn)行了回滾。
二、@Transactional 作用
@Transactional 可以用來(lái)修飾方法或類:
- 修飾方法時(shí): 只有修飾public 方法時(shí)才生效(修飾其他方法時(shí)不會(huì)報(bào)錯(cuò), 也不生效)[推薦]
- 修飾類時(shí): 對(duì) @Transactional 修飾的類中所有的 public 方法都生效。
方法/類被 @Transactional 注解修飾時(shí),在目標(biāo)方法執(zhí)行開始之前,會(huì)自動(dòng)開啟事務(wù),方法執(zhí)行結(jié)束之后,自動(dòng)提交事務(wù)。
- 如果在方法執(zhí)行過(guò)程中,出現(xiàn)異常,且異常未被捕獲,就進(jìn)行事務(wù)回滾操作。
- 如果異常被程序捕獲,方法就被認(rèn)為是成功執(zhí)行,依然會(huì)提交事務(wù)。
舉例:(對(duì)異常進(jìn)行捕獲)
@Transactional
@RequestMapping("/registry")
public String registry(String name,String password){
//??注冊(cè)
userService.registryUser(name,password);
log.info("??數(shù)據(jù)插?成功");
//對(duì)異常進(jìn)?捕獲
try {
//強(qiáng)制程序拋出異常
int a = 10/0;
}catch (Exception e){
e.printStackTrace();
}
return "注冊(cè)成功";
}運(yùn)行程序,發(fā)現(xiàn)雖然程序出錯(cuò)了,但是由于異常被捕獲,所以事務(wù)依然得到了提交。
如果需要事務(wù)進(jìn)行回滾,有以下兩種方式:
- 重新拋出異常
@Transactional
@RequestMapping("/registry")
public String registry(String name,String password){
//?戶注冊(cè)
userService.registryUser(name,password);
log.info("??數(shù)據(jù)插?成功");
//對(duì)異常進(jìn)?捕獲
try {
//強(qiáng)制程序拋出異常
int a = 10/0;
}catch (Exception e){
//將異常重新拋出去
throw e;
}
return "注冊(cè)成功";
}- 手動(dòng)回滾事務(wù)
使用TransactionAspectSupport.currentTransactionStatus() 得到當(dāng)前的事務(wù),并使用用 setRollbackOnly 設(shè)置 setRollbackOnly。
@Transactional
@RequestMapping("/registry")
public String registry(String name,String password){
//?戶注冊(cè)
userService.registryUser(name,password);
log.info("?戶數(shù)據(jù)插?成功");
//對(duì)異常進(jìn)行捕獲
try {
//強(qiáng)制程序拋出異常
int a = 10/0;
}catch (Exception e){
// 手動(dòng)回滾事務(wù)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return "注冊(cè)成功";
}三、@Transactional詳解
@Transactional注解有三個(gè)常見的屬性:
- rollbackFor: 異常回滾屬性。指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個(gè)異常類型。
- isolation: 事務(wù)的隔離級(jí)別。默認(rèn)值為 Isolation.DEFAULT
- propagation: 事務(wù)的傳播機(jī)制。默認(rèn)值為 Propagation.REQUIRED
3.1 rollbackFor
@Transactional 默認(rèn)只在遇到運(yùn)行時(shí)異常和Error時(shí)才會(huì)回滾,非運(yùn)行時(shí)異常不回滾。即
Exception的子類中,除了RuntimeException及其子類。

如果需要所有異常都回滾,需要來(lái)配置 @Transactional 注解當(dāng)中的 rollbackFor 屬性,通過(guò)rollbackFor 這個(gè)屬性指定出現(xiàn)何種異常類型時(shí)事務(wù)進(jìn)行回滾。
@Transactional(rollbackFor = Exception.class)
@RequestMapping("/r2")
public String r2(String name,String password) throws IOException {
//?戶注冊(cè)
userService.registryUser(name,password);
log.info("?戶數(shù)據(jù)插?成功");
if (true){
throw new IOException();
}
return "r2";
}運(yùn)行程序會(huì)發(fā)現(xiàn)雖然程序拋出了異常,但是事務(wù)依然進(jìn)行了提交。
3.2 事務(wù)隔離級(jí)別
事務(wù)隔離級(jí)別解決的是多個(gè)事務(wù)同時(shí)調(diào)用一個(gè)數(shù)據(jù)庫(kù)的問題:

Spring 中事務(wù)隔離級(jí)別有 5 種:
Isolation.DEFAULT: 以連接的數(shù)據(jù)庫(kù)的事務(wù)隔離級(jí)別為主;Isolation.READ_UNCOMMITTED: 讀未提交,對(duì)應(yīng)SQL標(biāo)準(zhǔn)中 READ UNCOMMITTED;Isolation.READ_COMMITTED: 讀已提交,對(duì)應(yīng)SQL標(biāo)準(zhǔn)中 READ COMMITTED;Isolation.REPEATABLE_READ: 可重復(fù)讀,對(duì)應(yīng)SQL標(biāo)準(zhǔn)中 REPEATABLE READ;Isolation.SERIALIZABLE: 串行化,對(duì)應(yīng)SQL標(biāo)準(zhǔn)中 SERIALIZABLE。
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 中事務(wù)隔離級(jí)別可以通過(guò) @Transactional 中的 isolation 屬性進(jìn)行設(shè)置:
@Transactional(isolation = Isolation.READ_COMMITTED)
@RequestMapping("/r3")
public String r3(String name,String password) throws IOException {
//...
return "r3";
}3.3 Spring 事務(wù)傳播機(jī)制
事務(wù)傳播機(jī)制就是: 多個(gè)事務(wù)方法存在調(diào)用關(guān)系時(shí),事務(wù)是如何在這些方法間進(jìn)行傳播的。
事務(wù)傳播機(jī)制解決的是一個(gè)事務(wù)在多個(gè)節(jié)點(diǎn)(方法)中傳遞的問題:

@Transactional 注解支持事務(wù)傳播機(jī)制的設(shè)置, 通過(guò) propagation 屬性來(lái)指定傳播行為。
Spring 事務(wù)傳播機(jī)制有以下 7 種:
Propagation.REQUIRED:默認(rèn)的事務(wù)傳播級(jí)別。如果當(dāng)前存在事務(wù), 則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù).Propagation.SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。Propagation.MANDATORY:強(qiáng)制性。如果當(dāng)前存在事務(wù), 則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則拋出異常。Propagation.REQUIRES_NEW: 創(chuàng)建一個(gè)新的事務(wù)。如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。也就是說(shuō)不管外部方法是否開啟事務(wù),Propagation.REQUIRES_NEW 修飾的內(nèi)部方法都會(huì)新開啟自己的事務(wù),且開啟的事務(wù)相互獨(dú)立,互不干擾。Propagation.NOT_SUPPORTED: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起(不用)。Propagation.NEVER: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù), 則拋出異常。Propagation.NESTED: 如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行。如果當(dāng)前沒有事務(wù),則該取值等價(jià)于 PROPAGATION_REQUIRED。
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;
}
}NESTED 和 REQUIRED 區(qū)別:
- 整個(gè)事務(wù)如果全部執(zhí)行成功,二者的結(jié)果是一樣的。
- 如果事務(wù)一部分執(zhí)行成功,REQUIRED加入事務(wù)會(huì)導(dǎo)致整個(gè)事務(wù)全部回滾。NESTED嵌套事務(wù)可以實(shí)現(xiàn)局部回滾,不會(huì)影響上一個(gè)方法中執(zhí)行的結(jié)果。
一個(gè)非常形象的比喻:
比如一對(duì)新人要結(jié)婚了, 關(guān)于是否需要房子:
- 1.
Propagation.REQUIRED: 需要有房子。 如果你有房,我們就一起住,如果你沒房,我們就一起買房。(如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)) - 2.
Propagation.SUPPORTS: 可以有房子。如果你有房, 那就一起住。如果沒房,那就租房。 (如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行) - 3.
Propagation.MANDATORY: 必須有房子。要求必須有房,如果沒房就不結(jié)婚。(如果當(dāng)前存在事務(wù), 則加?該事務(wù)。如果當(dāng)前沒有事務(wù),則拋出異常) - 4.
Propagation.REQUIRES_NEW: 必須買新房。不管你有沒有房,必須要兩個(gè)人一起買房。即使有房也不住。(創(chuàng)建一個(gè)新的事務(wù). 如果當(dāng)前存在事務(wù), 則把當(dāng)前事務(wù)掛起) - 5.
Propagation.NOT_SUPPORTED: 不需要房。不管你有沒有房, 我都不住,必須租房。(以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起) - 6.
Propagation.NEVER: 不能有房子。(以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常) - 7.
Propagation.NESTED: 如果你沒房,就一起買房。如果你有房,我們就以房子為根據(jù)地。做點(diǎn)生意。(如果如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行。如果當(dāng)前沒有事務(wù),則該取值等價(jià)于 PROPAGATION_REQUIRED )
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Spring中的編程式事務(wù)和聲明式事務(wù)
- MyBatisPlus+Spring實(shí)現(xiàn)聲明式事務(wù)的方法實(shí)現(xiàn)
- spring聲明式事務(wù)@Transactional開發(fā)常犯的幾個(gè)錯(cuò)誤及最新解決方案
- Spring中的聲明式事務(wù)控制詳解
- Spring聲明式事務(wù)@Transactional注解實(shí)現(xiàn)元數(shù)據(jù)驅(qū)動(dòng)的事務(wù)管理
- Spring @Transactional注解的聲明式事務(wù)簡(jiǎn)化業(yè)務(wù)邏輯中的事務(wù)管理
相關(guān)文章
Java連接postgresql數(shù)據(jù)庫(kù)的示例代碼
本篇文章主要介紹了Java連接postgresql數(shù)據(jù)庫(kù)的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
spring boot application properties配置實(shí)例代碼詳解
本文通過(guò)代碼給大家介紹了spring boot application properties配置方法,需要的的朋友參考下吧2017-07-07
Java的線程與進(jìn)程以及線程的四種創(chuàng)建方式
這篇文章主要為大家詳細(xì)介紹了Java的線程與進(jìn)程以及線程的四種創(chuàng)建方式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03
Java的JSON轉(zhuǎn)換類庫(kù)GSON的基礎(chǔ)使用教程
GSON是谷歌開源的一款Java對(duì)象與JSON對(duì)象互相轉(zhuǎn)換的類庫(kù),Java的JSON轉(zhuǎn)換類庫(kù)GSON的基礎(chǔ)使用教程,需要的朋友可以參考下2016-06-06
SpringBoot事務(wù)使用及回滾實(shí)現(xiàn)代碼詳解
這篇文章主要介紹了SpringBoot事務(wù)使用及回滾實(shí)現(xiàn)代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
springboot項(xiàng)目如何配置多數(shù)據(jù)源
本文介紹了如何在SpringBoot項(xiàng)目中配置多數(shù)據(jù)源,包括配置多個(gè)數(shù)據(jù)源、創(chuàng)建數(shù)據(jù)源配置類、配置事務(wù)管理器以及使用不同的Mapper,從而實(shí)現(xiàn)跨數(shù)據(jù)庫(kù)操作2025-03-03
Java面試Socket編程常用參數(shù)設(shè)置源碼問題分析
這篇文章主要為大家介紹了Java編程中關(guān)于Socket結(jié)構(gòu)分析,常用參數(shù)設(shè)置源碼示例以及面試中的問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03

