1. 程式人生 > 實用技巧 >Spring中的@Transactional事務註解

Spring中的@Transactional事務註解

一、事務特性

@Transactional註解是用於事務控制的,需要知道事務的ACID特徵:即原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、永續性(Durability)。

事務是用來控制資料的ACID特性的,用於保證資料的正確性和完整性。

@Transactional註解有兩種使用方式:

(1)標註在類上面:當作用於類上時,該類的所有public方法將都具有該型別的事務屬性,同時,我們也可以在方法級別使用該標註來覆蓋類級別的定義。

(2)標註在方法上面:當作用於方法上時,只有當該方法發生了異常才會進行回滾,其他的方法不受影響。

在專案中使用,@Transactional(rollbackFor=Exception.class),如果類加了這個註解,那麼這個類裡面的方法丟擲異常,就會回滾,資料庫裡面的資料也會回滾。在@Transactional註解中如果不配置rollbackFor屬性,那麼事物只會在遇到RuntimeException的時候才會回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非執行時異常時也回滾。


二、事務傳播行為與隔離級別

@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

(1)事物傳播行為介紹:

@Transactional(propagation=Propagation.REQUIRED) :如果有事務, 那麼加入事務, 沒有的話新建一個(預設情況下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不為這個方法開啟事務
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢後繼續執行老的事務
@Transactional(propagation=Propagation.MANDATORY) :必須在一個已有的事務中執行,否則丟擲異常
@Transactional(propagation=Propagation.NEVER) :必須在一個沒有的事務中執行,否則丟擲異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :如果其他bean呼叫這個方法,在其他bean中宣告事務那就用事務,如果其他bean沒有宣告事務,那就不用事務

(2)事物超時設定:

@Transactional(timeout=30) //預設是30秒

(3)事務隔離級別:

@Transactional(isolation = Isolation.READ_UNCOMMITTED):讀取未提交資料(會出現髒讀, 不可重複讀) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):讀取已提交資料(會出現不可重複讀和幻讀)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重複讀(會出現幻讀)
@Transactional(isolation = Isolation.SERIALIZABLE):序列化
    
MYSQL: 預設為REPEATABLE_READ級別
SQLSERVER: 預設為READ_COMMITTED  

髒讀 : 一個事務讀取到另一事務未提交的更新資料。
不可重複讀 : 在同一事務中,多次讀取同一資料返回的結果有所不同,即後續讀取可能讀到另一事務已提交的更新資料, 相反, "可重複讀"在同一事務中多次
讀取資料時能夠保證所讀資料一樣,即後續讀取不能讀到另一事務已提交的更新資料。
幻讀 : 一個事務讀到另一個事務已提交的insert資料。


三、spring支援程式設計式事務管理和宣告式事務管理兩種方式

(1)程式設計式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於程式設計式事務管理,spring推薦使用TransactionTemplate。
(2)宣告式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前建立或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。宣告式事務管理也有兩種常用的方式:一種是基於tx和aop名字空間的xml配置檔案;另一種是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。
使用註解形式的方式使用事務需要在xml配置檔案中配置如下資訊

<!-- 服務事務管理 -->
<bean id="demoTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="demoDataSource"/>
</bean>
 <!-- 啟用事務註解 -->
<tx:annotation-driven transaction-manager="demoTransactionManager"/>

四、需要注意的幾點如下

(1)@Transactional 註解只能被應用到public方法上,對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能。
(2)用spring事務管理器,由spring來負責資料庫的開啟、提交、回滾,預設遇到執行期異常(throw new RuntimeException("註釋");)會回滾,即遇到不受檢查(unchecked)的異常時執行回滾;而遇到需要捕獲的異常(throw new Exception("註釋");)不會回滾,即遇到受檢查的異常(就是非執行時丟擲的異常,編譯器會檢查到的異常叫受檢異常)時,需要我們指定方式來讓事務回滾。如果要想所有異常都回滾,則要加上@Transactional( rollbackFor=Exception.class),
如果讓unchecked異常不回滾: @Transactional(notRollbackFor=RunTimeException.class)。如下程式碼所示:

@Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾
public void methodName() {
   throw new Exception("註釋");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到執行期例外(throw new RuntimeException("註釋");)會回滾
public ItimDaoImpl getItemDaoImpl() {
   throw new RuntimeException("註釋");
}

(3)@Transactional 註解應該只被應用到public可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 但是這個被註解的方法將不具有事務功能。
(4)@Transactional 註解可以被應用於介面定義和介面方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 註解的出現不足於開啟事務行為,它僅僅是一種元資料,能夠被可以識別。上面的例子中,其實正是元素的出現開啟了事務行為。也就是說要有具體的bean出現時事務才會起作用。
(5)Spring團隊建議在具體的類(或類的方法)上使用@Transactional 註解,而不要使用在類所要實現的任何介面上。你當然可以在介面上使用 @Transactional 註解,但是這將只能當你設定了基於介面的代理時它才生效。因為註解是不能繼承的,這就意味著如果你正在使用基於類的代理時,那麼事務的設定將不能被基於類的代理所識別。因此,請接受Spring團隊的建議,必須接受啊!


五、@Transactional註解所具有的屬性

屬性 型別 描述
value String 可選的限定描述符,指定使用的事務管理器
propagation enum: Propagation 可選的事務傳播行為設定
isolation enum: Isolation 可選的事務隔離級別設定
readOnly boolean 讀寫或只讀事務,預設讀寫
timeout int (in seconds granularity) 事務超時時間設定
rollbackFor Class物件陣列,必須繼承自Throwable 導致事務回滾的異常類陣列
rollbackForClassName 類名陣列,必須繼承自Throwable 導致事務回滾的異常類名字陣列
noRollbackFor Class物件陣列,必須繼承自Throwable 不會導致事務回滾的異常類陣列
noRollbackForClassName 類名陣列,必須繼承自Throwable 不會導致事務回滾的異常類名字陣列

參考博文:
(1) https://www.cnblogs.com/clwydjgs/p/9317849.html (Spring中的@Transactional(rollbackFor = Exception.class)屬性詳解)
(2) https://www.cnblogs.com/caoyc/p/5632963.html (事物註解方式: @Transactional)
(3) https://blog.csdn.net/wo541075754/article/details/50717842 (mysql事務鎖表導致查詢超時)
(4) https://www.cnblogs.com/jasonboren/p/11361971.html (mysql事務特性)
(5) https://www.cnblogs.com/jasonboren/p/13366130.html (Java 異常)