
一、Spring中事務
Spring提供了兩種事務管理機制:編程式事務、聲明式事務
1.1、編程式事務
在代碼中手動的管理事務的提交、回滾等操作,代碼侵入性比較強
try {
//TODO something
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new InvoiceApplyException("異常失敗");
}
1.2、聲明式事務
基于AOP面向切面的,它將具體業務與事務處理部分解耦,代碼侵入性很低,所以在實際開發中聲明式事務用的比較多。
@Transactional
@GetMapping("/test")
public String test() {
int insert = cityInfoDictMapper.insert(cityInfoDict);
}
二、@Transactional
2.1、作用域
-
作用于類
當把@Transactional 注解放在類上時,表示所有該類的public方法都配置相同的事務屬性信息。 -
作用于方法
當類配置了@Transactional,方法也配置了@Transactional,方法的事務會覆蓋類的事務配置信息。 -
作用于接口
不推薦這種使用方法,因為一旦標注在Interface上并且配置了Spring AOP 使用CGLib動態代理,將會導致@Transactional注解失效
2.2、屬性
| 屬性 | 功能描述 |
|---|---|
| readOnly | 指定事務是否為只讀事務,默認值為 false; 為了忽略那些不需要事務的方法,比如讀取數據,可以設置 read-only 為 true。 【例如】:@Transactional(readOnly=true) |
| rollbackFor | 用于指定能夠觸發事務回滾的異常類型,可以指定多個異常類型。 【例如】 指定單一異常類:@Transactional(rollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
| rollbackForClassName | 用于指定能夠觸發事務回滾的異常類型 名稱,可以指定多個異常類型 名稱。 【例如】 指定單一異常類名稱:@Transactional(rollbackForClassName=“RuntimeException”) 指定多個異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”}) |
| noRollbackFor | 用于設置不需要進行回滾的異常類,可以指定多個異常類型 名稱。 【例如】 指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
| noRollbackForClassName | 用于設置不需要進行回滾的異常類 名稱,可以指定多個異常類型 名稱。 【例如】 指定單一異常類名稱:@Transactional(noRollbackForClassName=“RuntimeException”) 指定多個異常類名稱:@Transactional(noRollbackForClassName={“RuntimeException”,“Exception”}) |
| isolation | 事務的隔離級別,默認值為 Isolation.DEFAULT。 (1)Isolation.DEFAULT:使用底層數據庫默認的隔離級別。 (2)Isolation.READ_UNCOMMITTED:讀取未提交數據(會出現臟讀, 不可重復讀) 基本不使用 (3)Isolation.READ_COMMITTED:讀取已提交數據(會出現不可重復讀和幻讀) (4)Isolation.REPEATABLE_READ:可重復讀(會出現幻讀) Isolation.SERIALIZABLE:串行化 【例如】 @Transactional(isolation = Isolation.READ_UNCOMMITTED) |
| timeout | 該屬性用于設置事務的超時秒數,超過該時間限制但事務還沒有完成,則自動回滾事務。默認值為-1表示永不超時 【例如】 @Transactional(timeout=30) |
| propagation | 該屬性用于設置事務的傳播行為。默認是Propagation.REQUIRED (1)Propagation.REQUIRED:如果當前存在事務,則加入該事務,如果當前不存在事務,則創建一個新的事務。 (2)Propagation.SUPPORTS:如果當前存在事務,則加入該事務;如果當前不存在事務,則以非事務的方式繼續運行。 (3)Propagation.MANDATORY:如果當前存在事務,則加入該事務;如果當前不存在事務,則拋出異常。 (4)Propagation.REQUIRES_NEW:重新創建一個新的事務,如果當前存在事務,暫停當前的事務。 (5)Propagation.NOT_SUPPORTED:以非事務的方式運行,如果當前存在事務,暫停當前的事務。 (6)Propagation.NEVER:以非事務的方式運行,如果當前存在事務,則拋出異常。 (7)Propagation.NESTED :表示如果當前已經存在一個事務,那么該方法將會在嵌套事務中運行。嵌套的事務可以獨立于當前事務進行單獨地提交或回滾。如果當前事務不存在,那么其行為和 Propagation.REQUIRED 效果一樣。 【例如】 @Transactional(propagation=Propagation.REQUIRED) |
2.3、關于rollbackFor回滾的異常類型

(1)默認什么異常會回滾?
rollbackFor默認是error和RuntimeException會觸發回滾,其他異常或自定義異常不會回滾
(2)怎么看是否是運行時異常?
看其父類(或者“爺爺類”)是否是RuntimeException
三、SpringBoot配置全局異常處理
- 自定義Controller注解
@RestController
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public @interface MyController {
String value() default "";
}
在項目的Controller層,直接使用該注解即可(所有通過該Controller層的方法都會有事務)
- 自定義@Service
@Service
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public @interface MyService {
String value() default "";
}
一般不用,因為Controller層已經加了事務,再用這個可能因為事務嵌套(參考:事務嵌套導致Transaction rolled back because it has been marked as rollback-only)帶來問題。
四、失效場景
1、@Transactional 應用在非 public 修飾的方法上
如果Transactional注解應用在非public 修飾的方法上,Transactional將會失效。
原因是:TransactionInterceptor (事務攔截器)使用AOP,在目標方法執行前后進行攔截,會檢查目標方法的修飾符是否為 public,不是 public則不會獲取@Transactional 的屬性配置信息。(spring要求被代理方法必須是public的。)
2、@Transactional 注解屬性 propagation 設置錯誤
這種是因為propagation的參數設置錯誤,根據需求選擇合適的@Transactional的propagation屬性的值
3、@Transactional 注解屬性 rollbackFor 設置錯誤
rollbackFor默認是error和RuntimeException會觸發回滾,其他異常或自定義異常不會回滾,需要回滾則需要指定或者直接指定@Transactional(rollbackFor=Exception.class)
4、同一個類中方法調用,導致@Transactional失效
這個和redis緩存失效場景之一一樣,如果在一個類中,B方法加了事務,A方法沒加事務調用B方法,另外一個類調用A方法,則B方法的事務失效。這種情況可以加一層Mamager層對Service層通用能力的下沉。
5、異常被捕獲,導致@Transactional失效
當我們是用catch把異常捕獲了,那么該方法就不會拋出異常了,那么出現該異常就不會回滾了
6、數據庫引擎不支持事務
這種情況出現的概率并不高,事務能否生效數據庫引擎是否支持事務是關鍵。常用的MySQL數據庫默認使用支持事務的innodb引擎。一旦數據庫引擎切換成不支持事務的myisam,那事務就從根本上失效了。
7、沒有被 Spring 管理
如下面例子所示:
@Service
public class OrderService {
@Transactional
public void updateOrder(Order order) {
// update order
}
}
如果此時把 @Service 注解注釋掉,這個類就不會被加載成一個 Bean,那這個類就不會被 Spring 管理了,事務自然就失效了。
8、方法被定義為finall
方法被定義成了final的,這樣會導致spring aop生成的代理對象不能復寫該方法,而讓事務失效。
五、事務異常記錄
4.1、Transaction rolled back because it has been marked as rollback-only
事務嵌套導致Transaction rolled back because it has been marked as rollback-only




