Spring聲明式事務(wù)@Transactional的用法解讀
Spring 聲明式事務(wù) @Transactional的使用很簡單,只需要添加依賴,在需要的方法或者類上添加 @Transactional注解即可。
一、添加依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency>
在需要事務(wù)的方法上添加 @Transactional 注解就可以實現(xiàn)了,無需手動開啟事務(wù)和提交事務(wù),進(jìn)入方法時自動開啟事務(wù),方法執(zhí)行完會自動提交事務(wù),如果中途發(fā)生了沒有處理的異常會自動回滾事務(wù)。
代碼示例:
@RequestMapping("/trans") @RestController public class TransactionalController { @Autowired private UserService userService; @Transactional @RequestMapping("/registry") public String registry(String name,String password){ //用戶注冊 userService.registryUser(name,password); return "注冊成功"; } }
假設(shè)代碼有異常:
@Slf4j @RequestMapping("/trans") @RestController public class TransactionalController { @Autowired private UserService userService; @Transactional @RequestMapping("/registry") public String registry(String name,String password){ //?戶注冊 userService.registryUser(name,password); log.info("用戶數(shù)據(jù)插?成功"); //強(qiáng)制程序拋出異常 int a = 10/0; return "注冊成功"; } }
啟動項目發(fā)現(xiàn)雖然日志顯示數(shù)據(jù)插入成功,但數(shù)據(jù)庫卻沒有新增數(shù)據(jù),事務(wù)進(jìn)行了回滾。
二、@Transactional 作用
@Transactional 可以用來修飾方法或類:
- 修飾方法時: 只有修飾public 方法時才生效(修飾其他方法時不會報錯, 也不生效)[推薦]
- 修飾類時: 對 @Transactional 修飾的類中所有的 public 方法都生效。
方法/類被 @Transactional 注解修飾時,在目標(biāo)方法執(zhí)行開始之前,會自動開啟事務(wù),方法執(zhí)行結(jié)束之后,自動提交事務(wù)。
- 如果在方法執(zhí)行過程中,出現(xiàn)異常,且異常未被捕獲,就進(jìn)行事務(wù)回滾操作。
- 如果異常被程序捕獲,方法就被認(rèn)為是成功執(zhí)行,依然會提交事務(wù)。
舉例:(對異常進(jìn)行捕獲)
@Transactional @RequestMapping("/registry") public String registry(String name,String password){ //??注冊 userService.registryUser(name,password); log.info("??數(shù)據(jù)插?成功"); //對異常進(jìn)?捕獲 try { //強(qiáng)制程序拋出異常 int a = 10/0; }catch (Exception e){ e.printStackTrace(); } return "注冊成功"; }
運行程序,發(fā)現(xiàn)雖然程序出錯了,但是由于異常被捕獲,所以事務(wù)依然得到了提交。
如果需要事務(wù)進(jìn)行回滾,有以下兩種方式:
- 重新拋出異常
@Transactional @RequestMapping("/registry") public String registry(String name,String password){ //?戶注冊 userService.registryUser(name,password); log.info("??數(shù)據(jù)插?成功"); //對異常進(jìn)?捕獲 try { //強(qiáng)制程序拋出異常 int a = 10/0; }catch (Exception e){ //將異常重新拋出去 throw e; } return "注冊成功"; }
- 手動回滾事務(wù)
使用TransactionAspectSupport.currentTransactionStatus() 得到當(dāng)前的事務(wù),并使用用 setRollbackOnly 設(shè)置 setRollbackOnly。
@Transactional @RequestMapping("/registry") public String registry(String name,String password){ //?戶注冊 userService.registryUser(name,password); log.info("?戶數(shù)據(jù)插?成功"); //對異常進(jìn)行捕獲 try { //強(qiáng)制程序拋出異常 int a = 10/0; }catch (Exception e){ // 手動回滾事務(wù) TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return "注冊成功"; }
三、@Transactional詳解
@Transactional注解有三個常見的屬性:
- rollbackFor: 異?;貪L屬性。指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個異常類型。
- isolation: 事務(wù)的隔離級別。默認(rèn)值為 Isolation.DEFAULT
- propagation: 事務(wù)的傳播機(jī)制。默認(rèn)值為 Propagation.REQUIRED
3.1 rollbackFor
@Transactional 默認(rèn)只在遇到運行時異常和Error時才會回滾,非運行時異常不回滾。即
Exception的子類中,除了RuntimeException及其子類。
如果需要所有異常都回滾,需要來配置 @Transactional 注解當(dāng)中的 rollbackFor 屬性,通過rollbackFor 這個屬性指定出現(xiàn)何種異常類型時事務(wù)進(jìn)行回滾。
@Transactional(rollbackFor = Exception.class) @RequestMapping("/r2") public String r2(String name,String password) throws IOException { //?戶注冊 userService.registryUser(name,password); log.info("?戶數(shù)據(jù)插?成功"); if (true){ throw new IOException(); } return "r2"; }
運行程序會發(fā)現(xiàn)雖然程序拋出了異常,但是事務(wù)依然進(jìn)行了提交。
3.2 事務(wù)隔離級別
事務(wù)隔離級別解決的是多個事務(wù)同時調(diào)用一個數(shù)據(jù)庫的問題:
Spring 中事務(wù)隔離級別有 5 種:
Isolation.DEFAULT
: 以連接的數(shù)據(jù)庫的事務(wù)隔離級別為主;Isolation.READ_UNCOMMITTED
: 讀未提交,對應(yīng)SQL標(biāo)準(zhǔn)中 READ UNCOMMITTED;Isolation.READ_COMMITTED
: 讀已提交,對應(yīng)SQL標(biāo)準(zhǔn)中 READ COMMITTED;Isolation.REPEATABLE_READ
: 可重復(fù)讀,對應(yīng)SQL標(biāo)準(zhǔn)中 REPEATABLE READ;Isolation.SERIALIZABLE
: 串行化,對應(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ù)隔離級別可以通過 @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ī)制就是: 多個事務(wù)方法存在調(diào)用關(guān)系時,事務(wù)是如何在這些方法間進(jìn)行傳播的。
事務(wù)傳播機(jī)制解決的是一個事務(wù)在多個節(jié)點(方法)中傳遞的問題:
@Transactional 注解支持事務(wù)傳播機(jī)制的設(shè)置, 通過 propagation 屬性來指定傳播行為。
Spring 事務(wù)傳播機(jī)制有以下 7 種:
Propagation.REQUIRED
:默認(rèn)的事務(wù)傳播級別。如果當(dāng)前存在事務(wù), 則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù).Propagation.SUPPORTS
:如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。Propagation.MANDATORY
:強(qiáng)制性。如果當(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。
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ū)別:
- 整個事務(wù)如果全部執(zhí)行成功,二者的結(jié)果是一樣的。
- 如果事務(wù)一部分執(zhí)行成功,REQUIRED加入事務(wù)會導(dǎo)致整個事務(wù)全部回滾。NESTED嵌套事務(wù)可以實現(xiàn)局部回滾,不會影響上一個方法中執(zhí)行的結(jié)果。
一個非常形象的比喻:
比如一對新人要結(jié)婚了, 關(guān)于是否需要房子:
- 1.
Propagation.REQUIRED
: 需要有房子。 如果你有房,我們就一起住,如果你沒房,我們就一起買房。(如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)) - 2.
Propagation.SUPPORTS
: 可以有房子。如果你有房, 那就一起住。如果沒房,那就租房。 (如果當(dāng)前存在事務(wù),則加入該事務(wù)。如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行) - 3.
Propagation.MANDATORY
: 必須有房子。要求必須有房,如果沒房就不結(jié)婚。(如果當(dāng)前存在事務(wù), 則加?該事務(wù)。如果當(dāng)前沒有事務(wù),則拋出異常) - 4.
Propagation.REQUIRES_NEW
: 必須買新房。不管你有沒有房,必須要兩個人一起買房。即使有房也不住。(創(chuàng)建一個新的事務(wù). 如果當(dāng)前存在事務(wù), 則把當(dāng)前事務(wù)掛起) - 5.
Propagation.NOT_SUPPORTED
: 不需要房。不管你有沒有房, 我都不住,必須租房。(以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起) - 6.
Propagation.NEVER
: 不能有房子。(以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常) - 7.
Propagation.NESTED
: 如果你沒房,就一起買房。如果你有房,我們就以房子為根據(jù)地。做點生意。(如果如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行。如果當(dāng)前沒有事務(wù),則該取值等價于 PROPAGATION_REQUIRED )
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java連接postgresql數(shù)據(jù)庫的示例代碼
本篇文章主要介紹了Java連接postgresql數(shù)據(jù)庫的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08spring boot application properties配置實例代碼詳解
本文通過代碼給大家介紹了spring boot application properties配置方法,需要的的朋友參考下吧2017-07-07Java的線程與進(jìn)程以及線程的四種創(chuàng)建方式
這篇文章主要為大家詳細(xì)介紹了Java的線程與進(jìn)程以及線程的四種創(chuàng)建方式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03Java的JSON轉(zhuǎn)換類庫GSON的基礎(chǔ)使用教程
GSON是谷歌開源的一款Java對象與JSON對象互相轉(zhuǎn)換的類庫,Java的JSON轉(zhuǎn)換類庫GSON的基礎(chǔ)使用教程,需要的朋友可以參考下2016-06-06SpringBoot事務(wù)使用及回滾實現(xiàn)代碼詳解
這篇文章主要介紹了SpringBoot事務(wù)使用及回滾實現(xiàn)代碼詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08Java面試Socket編程常用參數(shù)設(shè)置源碼問題分析
這篇文章主要為大家介紹了Java編程中關(guān)于Socket結(jié)構(gòu)分析,常用參數(shù)設(shè)置源碼示例以及面試中的問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03