天天看點

事務注解@Transactional又失效了!

作者:晾幹的紅領巾
事務注解@Transactional又失效了!

1、相關基本知識

1.1事務的傳播方式

//如果有事務, 那麼加入事務, 沒有的話建立一個(預設)
@Transactional(propagation=Propagation.REQUIRED)
//容器不為這個方法開啟事務 
@Transactional(propagation=Propagation.NOT_SUPPORTED)
//不管是否存在事務, 都建立一個新的事務, 原來的挂起, 新的執行完畢, 繼續執行老的事務 
@Transactional(propagation=Propagation.REQUIRES_NEW) 
//必須在一個已有的事務中執行, 否則抛出異常
@Transactional(propagation=Propagation.MANDATORY) 
//必須在一個沒有的事務中執行, 否則抛出異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER) 
//如果其他bean調用這個方法, 在其他bean中聲明事務, 那就用事務, 如果其他bean沒有聲明事務, 那就不用事務
@Transactional(propagation=Propagation.SUPPORTS) 
複制代碼           

1.2事務的隔離級别

// 讀取未送出資料(會出現髒讀, 不可重複讀) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 讀取已送出資料(會出現不可重複讀和幻讀) Oracle預設
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重複讀(會出現幻讀) MySQL預設
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE)
複制代碼           

ISOLATION_DEFAULT: 使用後端資料庫預設的隔離級别,Mysql 預設采用的 REPEATABLE_READ隔離級别 Oracle 預設采用的 READ_COMMITTED隔離級别.

ISOLATION_READ_UNCOMMITTED: 最低的隔離級别,允許讀取尚未送出的資料變更,可能會導緻髒讀、幻讀或不可重複讀

ISOLATION_READ_COMMITTED: 允許讀取并發事務已經送出的資料,可以阻止髒讀,但是幻讀或不可重複讀仍有可能發生

ISOLATION_REPEATABLE_READ: 對同一字段的多次讀取結果都是一緻的,除非資料是被本身事務自己所修改,可以阻止髒讀和不可重複讀,但幻讀仍有可能發生。

ISOLATION_SERIALIZABLE: 最高的隔離級别,完全服從ACID的隔離級别。所有的事務依次逐個執行,這樣事務之間就完全不可能産生幹擾,也就是說,該級别可以防止髒讀、不可重複讀以及幻讀。但是這将嚴重影響程式的性能。通常情況下也不會用到該級别。

1.3其他屬性

事務注解@Transactional又失效了!
  • timeout 事務的逾時時間,預設值為 -1。如果超過該時間限制但事務還沒有完成,則自動復原事務。
  • readOnly 指定事務是否為隻讀事務,預設值為 false;為了忽略那些不需要事務的方法,比如讀取資料,可以設定 read-only 為 true。
  • rollbackFor 用于指定能夠觸發事務復原的異常類型,可以指定多個異常類型。
  • noRollbackFor 抛出指定的異常類型,不復原事務,也可以指定多個異常類型。

2、事務失效場景

2.1未被Spring管理

事務方法所在類并沒有被Spring管理,則Spring事務會失效。這種對于Springboot項目其實不會的,你把@Service注釋掉,Controller中注入UserService是會報錯的。

事務注解@Transactional又失效了!

2.2方法不能被重寫

這裡的意思是帶@Transactional注釋的方法必須是可重寫的,那我們知道private、static、final修飾的方法均不能被重寫。

事務注解@Transactional又失效了!

2.3同一個類中方法互相調用

這裡我們在同一個類中方法A調用了方法B,在方法B上使用@Transactional,此時方法B中出現異常,我們是希望他能復原的。

事務注解@Transactional又失效了!

可惜并沒有復原事務

事務注解@Transactional又失效了!

聲明式事務實作原理是面向切面程式設計,通過cglib建立代理proxy,當我們通路帶 @Transactional方法,如果通過spring容器擷取bean,實際通路的是代理對象,代理對象已經在帶 @Transactional方法前後增加了事務相關的邏輯。而當調用帶 @Transactional方法的調用方是同類方法時,調用的是this對象的方法,沒有通過spring容器擷取bean,就無法通路到代理對象,事務也就沒有生效。

我們把@Transactional注解放到方法A上就可以了。此時即便方法B是private修飾的也無所謂

事務注解@Transactional又失效了!

2.4異常被catch了

基于2.3的例子,假如把方法B中的異常catch了卻沒有抛出異常,事務也是不會復原的。

事務注解@Transactional又失效了!

解決辦法:通常我們會在Springboot項目中自定義一個異常,在catch語句塊中抛出自定義異常即可。

2.5rollbackFor使用不當

rollbackFor預設值為UncheckedException,包括了RuntimeException和Error。

在使用@Transactional注解時不指定rollbackFor,Exception及其子類都不會觸發復原。

事務注解@Transactional又失效了!

繼承自Runtime Exception或 Error 的是非檢查型異常,而繼承自 Exception 的則是檢查型異常。

2.6資料庫引擎不支援事務

比如MySQL資料庫選用MyISAM存儲引擎,而MyISAM存儲引擎本身不支援事務,事務肯定不會生效。

2.7多線程問題

如果你去面試,面試官問你多線程事務如何復原,你要是回答用@Transactional注解,就可以直接回去了。Spring的事務是通過ThreadLocal來保證線程安全的,事務和目前線程綁定。

繼續閱讀