@Transactional
事務管理的目的
- 在出現異常的情況下,保證數據的一致性;數據提交操作回滾至異常發生前的狀態
事務管理的方式:
- Spring(Spring Framework 提供對事務管理的抽象接口) 支持兩種事務管理方式:
- 編程式事務管理:使用TransactionTemplate或PlatformTransactionManager實現
- 聲明式事務管理:建立在AOP之上的。其本質是對方法前后進行攔截,然后在目標方法開始之前創建或者加入一個事務(此處取決於事務的傳播行為),在執行完目標方法之后根據執行情況提交或者回滾事務(執行成功則提交,失敗則進行實物的回滾)
- 編程式事務管理優勢:可以控制事務的粒度,最細粒度到代碼塊級別;
- 聲明式實物管理優勢:在方法外進行聲明,事務控制的代碼不會與業務邏輯代碼混在一起,最細粒度到方法級別(解決方法:可以將需要進行事務管理的代碼塊獨立為方法,通過方法間調用實現);符合spring倡導的非侵入式的開發方式,即業務處理邏輯代碼與事務管理代碼不放在一起
聲明式事務管理實現方式:
- 基於tx和aop名字空間的xml配置文件
// 基本配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.1.xsd"> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="shardingDataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> // MyBatis自動參與到spring事務管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的數據源與DataSourceTransactionManager引用的數據源一致即可,否則事務管理會不起作用 // <annotation-driven>標簽的聲明,則是在Spring內部啟用@Transactional來進行事務管理,使用 @Transactional 前需要配置
- 基於@Transactional注解
@Transactional實質是使用了JDBC的事務來進行事務控制的
@Transactional基於Spring的動態代理的機制 - @Transactional實現原理
- 1)事務開始時,通過AOP機制,生成一個代理connection對象,並將其放入DataSource實例的某個與DataSourceTransactionManager相關的某處容器中。在接下來的整個事務中,客戶代碼都應該使用該connection連接數據庫,執行所有數據庫命令[不使用該connection連接數據庫執行的數據庫命令,在本事務回滾的時候得不到回滾](物理連接connection邏輯上新建一個會話session;DataSource與TransactionManager配置相同的數據源)
- 2)事務結束時,回滾在第1步驟中得到的代理connection對象上執行的數據庫命令,然后關閉該代理connection對象(事務結束后,回滾操作不會對已執行完畢的SQL操作命令起作用)
聲明式事務的管理實現本質:
- 事務的兩種開啟方式
- 顯示開啟
start transaction | begin,通過commit | rollback結束事務 - 關閉數據庫中自動提交 autocommit
set autocommit = 0;MySQL 默認開啟自動提交;通過手動提交或執行回滾操作來結束事務
- 顯示開啟
- Spring 關閉數據庫中自動提交:在方法執行前關閉自動提交,方法執行完畢后再開啟自動提交
// org.springframework.jdbc.datasource.DataSourceTransactionManager.java 源碼實現
// switch to manual commit if necessary. this is very expensive in some jdbc drivers, // so we don't want to do it unnecessarily (for example if we've explicitly // configured the connection pool to set it already). if (con.getautocommit()) { txobject.setmustrestoreautocommit(true); if (logger.isdebugenabled()) { logger.debug("switching jdbc connection [" + con + "] to manual commit"); } con.setautocommit(false); }
-
問題:
- 關閉自動提交后,若事務一直未完成,即未手動執行 commit 或 rollback 時如何處理已經執行過的SQL操作?
- C3P0默認的策略是回滾任何未提交的事務
- C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規范和JDBC2的標准擴展。目前使用它的開源項目有Hibernate,Spring等
- JNDI(Java Naming and Directory Interface,Java命名和目錄接口)是SUN公司提供的一種標准的Java命名系統接口,JNDI提供統一的客戶端API,通過不同的訪問提供者接口JNDI服務供應接口(SPI)的實現,由管理者將JNDI API映射為特定的命名服務和目錄系統,使得Java應用程序可以和這些命名服務和目錄服務之間進行交互
- 關閉自動提交后,若事務一直未完成,即未手動執行 commit 或 rollback 時如何處理已經執行過的SQL操作?
spring事務特性
- spring所有的事務管理策略類都繼承自
org.springframework.transaction.PlatformTransactionManager接口
public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
-
事務的隔離級別:是指若干個並發
的事務之間的隔離程度- @Transactional(isolation = Isolation.READ_UNCOMMITTED):讀取未提交數據(會出現臟讀, 不可重復讀) 基本不使用
- @Transactional(isolation = Isolation.READ_COMMITTED):讀取已提交數據(會出現不可重復讀和幻讀)
- @Transactional(isolation = Isolation.REPEATABLE_READ):可重復讀(會出現幻讀)
- @Transactional(isolation = Isolation.SERIALIZABLE):串行化
-
事務傳播行為:如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為
- TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。這是默認值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
@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 | 不會導致事務回滾的異常類名字數組 |
說明:
- value :主要用來指定不同的事務管理器;主要用來滿足在同一個系統中,存在不同的事務管理器。比如在Spring中,聲明了兩種事務管理器txManager1, txManager2.然后,用戶可以根據這個參數來根據需要指定特定的txManager.
- value 適用場景:在一個系統中,需要訪問多個數據源或者多個數據庫,則必然會配置多個事務管理器的
- REQUIRED_NEW和NESTED兩種不同的傳播機制的區別
- REQUIRED_NEW:內部的事務獨立運行,在各自的作用域中,可以獨立的回滾或者提交;而外部的事務將不受內部事務的回滾狀態影響
- ESTED的事務,基於單一的事務來管理,提供了多個保存點。這種多個保存點的機制允許內部事務的變更觸發外部事務的回滾。而外部事務在混滾之后,仍能繼續進行事務處理,即使部分操作已經被混滾。 由於這個設置基於JDBC的保存點,所以只能工作在JDBC的機制
- rollbackFor : 讓受檢查異常回滾;即讓本來不應該回滾的進行回滾操作
- noRollbackFor :忽略非檢查異常;即讓本來應該回滾的不進行回滾操作
spring事務回滾規則
- 指示spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內拋出異常
spring事務管理器會捕捉任何未處理的異常,然后依據規則決定是否回滾拋出異常的事務
- 默認配置下,spring只有在拋出的異常為運行時unchecked異常時才回滾該事務,也就是拋出的異常為RuntimeException的子類(Errors也會導致事務回滾),而拋出checked異常則不會導致事務回滾。
- 用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.默認遇到運行期例外(throw new RuntimeException(“注釋”);)會回滾,即遇到不受檢查(unchecked)的例外時回滾;而遇到需要捕獲的例外(throw new Exception(“注釋”);)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需我們指定方式來讓事務回滾要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .如果讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
注意事項:
-
@Transactional 使用位置 類上方、方法上方
Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基於接口的代理時它才會生效
當作用於類上時,該類的所有 public 方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。 -
方法的訪問權限為 public
@Transactional 注解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。在 protected、private 或者默認可見性的方法上使用 @Transactional 注解,這將被忽略,也不會拋出任何異常 -
默認情況下,只有來自外部的方法調用才會被AOP代理捕獲,也就是,類內部方法調用本類內部的其他方法並不會引起事務行為,即使被調用方法使用@Transactional注解進行修飾
例如一:同一個類中方法,A方法未使用此標簽,B使用了,C未使用,A 調用 B , B 調用 C ;則外部調用A之后,B的事務是不會起作用的
例如二:若是有上層(按照 Controller層、Service層、DAO層的順序)由Action 調用 Service 直接調用,發生異常會發生回滾;若間接調用,Action 調用 Service 中 的A 方法,A無@Transactional 注解,B有,A調用B,B的注解無效
其他
- 事務方法的嵌套調用會產生事務傳播
- spring 的事務管理是線程安全的
- 父類的聲明的@Transactional會對子類的所有方法進行事務增強;子類覆蓋重寫父類方式可覆蓋其@Transactional中的聲明配置
- 類名上方使用@Transactional,類中方法可通過屬性配置覆蓋類上的@Transactional配置;比如:類上配置全局是可讀寫,可在某個方法上改為只讀
