1. 程式人生 > >Spring事務管理總結

Spring事務管理總結

需求 應用 def 關系數據庫 stat 啟動 全部 test data

事務定義

  事務管理對於企業應用來說是至關重要的,主要作用是用來保證數據的一致性,比如轉賬問題。如下偽代碼所示:

transaction begin
    A賬戶扣100
    B賬戶加100
transaction commit

  要麽這兩部操作都完成,要麽都不做(原子性),否則數據就不完整。因為在一個事務中的操作可以看成是一個統一整體,所以事務可以定義為一個不可分割的工作單元

  一個事務可以以兩種方式結束:提交或者回滾。當事務提交時,所有對數據的修改都會保存。如果事務中的某一部操作失敗了,則事務回滾,並且在事務中的所有對數據的改動全部取消。所以即便是事務失敗了,數據的完整性依然能夠保證(一致性)。

事務四大特性(ACID)

  • 原子性(Atomicity):原子性是指事務是一個不可分割的工作單位,事務中的操作要麽全部成功,要麽全部失敗。
  • 一致性(Consistency):事務在完成時,必須使所有的數據都保持一致狀態(事務前後狀態是一致的)。還是用上面的例子,A賬戶有1000元,B賬戶有500元,加起來是1500,A向B轉賬100元,事務結束後,A是900元,B是600元,加起來還是1500元,即使中間出問題了,事務回退,A和B的總余額還是1500元,這就是一致性。
  • 隔離性(Isolation):由並發事務所做的修改必須與任何其它並發事務所作的修改隔離。
  • 持久性(Durability):事務完成後,對系統的影響是永久性的。

Spring事務管理

  對事務的支持是Spring的一大特性,Spring對其事務管理提供了一致地抽象,具有如下優點:

  • 為不同的事務管理API如JTA(Java Transaction API)、JDBC、Hibernate、JPA(Java Persistence API)、JDO(Java Data Objects)提供了一致地編程模型;
  • 支持聲明式事務管理;
  • 提供比JTA更簡單易用地編程式事務管理API;
  • 和Spring數據訪問抽象的完美集成;

什麽是Spring提供的一致編程模型(consistent programming model)?

  在Spring之前Java EE開發人員可以通過兩種方式來進行事務管理:全局事務和局部事務。全局事務允許處理多個事務源,典型關系數據庫和消息隊列。應用服務器通過JTA(Java Transaction API)來管理全局事務,而JTA又需要用到JNDI,通常JTA只能在應用服務器環境下使用,因此全局事務會限制代碼的復用性。而局部事務比如JDBC事務,雖然比較簡單,可以實現最基本的事務操作,但是只能處理同一個數據源的操作,如果涉及到多數據的操作或者分布式場景,JDBC事務就無能為力了。

  Spring通過提供一個一致的編程模型來解決全局事務和局部事務的缺點,所謂的一致的編程模型其實可以理解為Spring對事務的抽象,即Spring只提供了事務管理器的接口:org.springframework.transaction.PlatformTransactionManager,通過這個接口,Spring為各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,各個平臺自己負責實現具體的事務管理,Spring官方的說法叫事務策略(transaction strategy)。PlatformTransactionManager接口如下:

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

  getTransaction(..)方法根據傳入的TransactionDefinition參數返回了一個TransactionStatus對象,TransactionStatus代表一個新的事務或者一個已存在的事務。TransactionDefinition是一個接口,主要設置事務隔離級別、傳播特性、超時間隔和是否只讀。

  不管是使用聲明式還是編程式事務管理,都需要設置具體的事務管理器(PlatformTransactionManager接口的實現),在Spring中通過依賴註入的方式來指定,springboot會根據具體的依賴完成自動註入。

  Spring提供了許多內置事務管理器實現,常用的有HibernateTransactionManager和DataSourceTransactionManager:

  • DataSourceTransactionManager:數據源事務管理器,提供對單個javax.sql.DataSource事務管理,用於Spring JDBC抽象框架、iBATIS或MyBatis框架的事務管理;
  • HibernateTransactionManager:提供對單個org.hibernate.SessionFactory事務支持,用於集成Hibernate框架時的事務管理;
  • JtaTransactionManager:提供對分布式事務管理的支持,並將事務管理委托給Java EE應用服務器事務管理器;
  • JpaTransactionManager:提供對單個javax.persistence.EntityManagerFactory事務支持,用於集成JPA實現框架時的事務管理;
  • JdoTransactionManager:提供對單個javax.jdo.PersistenceManagerFactory事務管理,用於集成JDO框架時的事務管理;

聲明式事務管理和編程式事務管理

  spring支持編程式事務管理和聲明式事務管理兩種方式。

  編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,spring推薦使用TransactionTemplate。

  聲明式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前創建或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。聲明式事務最大的優點就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需在配置文件中做相關的事務規則聲明(或通過基於@Transactional註解的方式),便可以將事務規則應用到業務邏輯中。

  顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受汙染,一個普通的POJO對象,只要加上註解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,後者的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立為方法等等。

  聲明式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置文件,另一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。

Spring聲明式事務的配置

  Spring Boot 使用事務非常簡單,首先在啟動類上添加@EnableTransactionManagement 註解,啟註解事務管理,這等同於xml配置方式的 <tx:annotation-driven />。然後在訪問數據庫的Service方法上添加註解 @Transactional 即可。

  在Spring Boot中,關於事務管理器,不管是JPA還是JDBC等都實現自接口 PlatformTransactionManager。當我們使用了spring-boot-starter-jdbc的時候,Spring Boot會自動註入DataSourceTransactionManager啟用幫助配置數據庫事務相關的類;如果你添加的是 spring-boot-starter-data-jpa 依賴,框架會默認註入 JpaTransactionManager 實例。

  @Transactional 註解可以作用於接口、接口方法、類以及類方法上,作用於單個方法上時該方法將支持事務;作用在類上時,該類的所有 public 方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標註來覆蓋類級別的定義。但是 Spring 建議不要在接口或者接口方法上使用該註解,因為這只有在使用基於接口的代理時它才會生效。另外, @Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private 或者默認可見性的方法上使用 @Transactional 註解,這將被忽略,也不會拋出任何異常。

  在@Transactional 後可以設置屬性,來設置事務的具體語義,有如下屬性:

value:類型是字符串,可以設置具體的事務管理器,如@Transactional(value="txManager1")

  大多數應用只需單個事務管理器,但是有些場合需要多個事務管理器,則需要人為的指定使用哪個事務管理器。可以在啟動類中添加如下方法,Debug測試,就能知道自動註入的是 PlatformTransactionManager 接口的哪個實現類。

 @Bean
 public Object testBean(PlatformTransactionManager platformTransactionManager) {
   System.out.println("------>>" + platformTransactionManager.getClass().getName());
    return new Object();
 }

propagation:類型為枚舉,用於設置事務的傳播行為,例如@Transactional(propagation = Propagation .REQUIRED )

  1. REQUIRED :如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
  2. SUPPORTS :如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
  3. MANDATORY :如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
  4. REQUIRES_NEW :創建一個新的事務,如果當前存在事務,則把當前事務掛起。
  5. NOT_SUPPORTED :以非事務方式運行,如果當前存在事務,則把當前事務掛起。
  6. NEVER :以非事務方式運行,如果當前存在事務,則拋出異常。
  7. NESTED :如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於 REQUIRED

isolation:類型為枚舉,用於設置事務的傳播特性,例如:@Transactional(isolation = Isolation.DEFAULT)

  1. DEFAULT :這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是: READ_COMMITTED 。
  2. READ_UNCOMMITTED :該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別。
  3. READ_COMMITTED :該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。
  4. REPEATABLE_READ :該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,並且每次返回的記錄都相同。即使在多次查詢之間有新增的數據滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復讀。
  5. SERIALIZABLE :所有的事務依次逐個執行,這樣事務之間就完全不可能產生幹擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。

readOnly:類型為boolean,用於設置事務為只讀或讀寫

timeout:類型為int,用於設置事務的超時時間

rollbackFor:一系列類,用於設置出現某些異常(繼承自Throwable)時一定觸發事務回退,如@Transactional(rollbackFor=Exception.class)

rollbackForClassName:一系列類名,用於設置出現某些異常(繼承自Throwable)時一定觸發事務回退

noRollbackFor:一系列類,用於設置出現某些異常(繼承自Throwable)時一定不會觸發事務回退

noRollbackForClassName:一系列類名,用於設置出現某些異常(繼承自Throwable)時一定不會觸發事務回退

  如果未設置事務屬性,則spring會為事務指定默認屬性,默認傳播行為:PROPAGATION_REQUIRED,默認隔離級別:ISOLATION_DEFAULT,事務為讀寫,默認RuntimeException會觸發回退,而檢查異常則不會觸發回退。

參考文獻:

Transaction Management

What Is a Transaction?

Spring事務管理總結