Spring實踐--spring事務:基礎知識

一:基礎知識介紹

1.1:事務四個特性:ACID

  • 原子性(Atomicity):事務是一個原子操做,由一系列動做組成。事務的原子性確保動做要麼所有完成,要麼所有失敗。
  • 一致性(Consistency):一旦事務完成(無論成功仍是失敗),系統必須確保它所建模的業務處於一致的狀態,而不會是部分完成部分失敗。在現實中的數據不該該被破壞。
  • 隔離性(Isolation):可能有許多事務會同時處理相同的數據,所以每一個事務都應該與其餘事務隔離開來,防止數據損壞。
  • 持久性(Durability):一旦事務完成,不管發生什麼系統錯誤,它的結果都不該該受到影響,這樣就能從任何系統崩潰中恢復過來。一般狀況下,事務的結果被寫到持久化存儲器中。

1.2 隔離級別錯誤說明

  1. 髒讀:併發事務之間一個事務讀取了另外一個事務還未提交的數據。
  2. 不可重複讀:併發事務間一個事務屢次讀取,分別讀取了另外一個事務未提交和已經提交的數據,致使屢次讀取的數據狀態不一致。
  3. 幻讀:併發事務間一個事務讀取了另外一個事務已經提交的數據。與不可重複讀的區別是不可重複讀先後讀取的是同一數據項,而幻讀是一批數據。好比先後讀取的數據數量不一致。

1.3 數據庫四大隔離級別

  1. Serializable(串行化):可避免髒讀、不可重複讀、幻讀的發生。
  2. Repeatable Read(可重複讀):可避免髒讀、不可重複讀的發生。
  3. Read Committed(讀已提交):可避免髒讀的發生。
  4. Read unCommitted:(讀未提交):最低級別,任何狀況都會發生。

1.4 Spring隔離級別屬性

  1. TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,一般這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務能夠讀取另外一個事務修改但尚未提交的數據。該級別不能防止髒讀,不可重複讀和幻讀,所以不多使用該隔離級別。
  3. TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另外一個事務已經提交的數據。該級別能夠防止髒讀,這也是大多數狀況下的推薦值。
  4. TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程當中能夠屢次重複執行某個查詢,而且每次返回的記錄都相同。該級別能夠防止髒讀和不可重複讀。
  5. TransactionDefinition.ISOLATION_SERIALIZABLE:全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。

1.5 Spring事務的傳播行爲

事務的第一個方面是傳播行爲(propagation behavior)。當事務方法被另外一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在本身的事務中運行。html

Spring定義了七種傳播行爲:java

  1. TransactionDefinition.PROPAGATION_REQUIRED:若是當前事務存在,則加入當前事務;若是不存在,則建立一個新的事務。通常默認此項。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前事務存在,則掛起當前事務。
  3. TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事物;若是事務不存在,則以無事務方式執行。
  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式執行,若是當前事務存在,則掛起當前事務。
  5. TransactionDefinition.PROPAGATION_NEVER:以非事務方式執行,若是存在事務,則拋出異常。
  6. TransactionDefinition.PROPAGATION_MANDATORY:表示該方法必須在事務中運行,若是當前存在事務,則加入當前事務;若是不存在,則拋出異常。
  7. TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個新的事務做爲當前事務內嵌事務執行,嵌套的事務能夠獨立於當前事務進行單獨地提交或回滾。若是不存在,則取該值等價於TransactionDefinition.PROPAGATION_REQUIRED,即建立一個新的事務

注:如下具體講解傳播行爲的內容參考自Spring事務機制詳解mysql

此@Transactional註解來自org.springframework.transaction.annotation包,而不是javax.transaction。程序員

1.6 Spring事務分類

  • 聲明式事務:即爲在配置文件中配置,無需程序員手動編程控制事務,也就是說數據庫的事務的開啓,提交都是框架幫助咱們作好的
  • 編程式事務:是須要在方法中加入Spring的事務API  例如hibernate中的  beginTransaction() commit() rollback, 更加具備細粒度,可是同時也增長了代碼的傾入性!

二:核心接口

Spring事務管理的實現有許多細節,若是對整個接口框架有個大致瞭解會很是有利於咱們理解事務,下面經過講解Spring的事務接口來了解Spring實現事務的具體策略。 
Spring事務管理涉及的接口的聯繫以下:spring

2.1 事務管理器

Spring並不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委託給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。 
Spring事務管理器的接口是org.springframework.transaction.PlatformTransactionManager,經過這個接口,Spring爲各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,可是具體的實現就是各個平臺本身的事情了。此接口的內容以下:sql

Public interface PlatformTransactionManager()...{  
    // 由TransactionDefinition獲得TransactionStatus對象
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滾
    Void rollback(TransactionStatus status) throws TransactionException;  
} 

從這裏可知具體的具體的事務管理機制對Spring來講是透明的,它並不關心那些,那些是對應各個平臺須要關心的,因此Spring事務管理的一個優勢就是爲不一樣的事務API提供一致的編程模型,如JTA、JDBC、Hibernate、JPA。下面分別介紹各個平臺框架實現事務管理的機制。數據庫

2.1.1 JDBC事務

若是應用程序中直接使用JDBC來進行持久化,DataSourceTransactionManager會爲你處理事務邊界。爲了使用DataSourceTransactionManager,你須要使用以下的XML將其裝配到應用程序的上下文定義中:編程

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
<property name="dataSource" ref="dataSource" />  
</bean>

實際上,DataSourceTransactionManager是經過調用java.sql.Connection來管理事務,然後者是經過DataSource獲取到的。經過調用鏈接的commit()方法來提交事務,一樣,事務失敗則經過調用rollback()方法進行回滾。session

2.1.2 Hibernate事務

若是應用程序的持久化是經過Hibernate實習的,那麼你須要使用HibernateTransactionManager。對於Hibernate3,須要在Spring上下文定義中添加以下的<bean>聲明:併發

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
<property name="sessionFactory" ref="sessionFactory" />  
</bean>

sessionFactory屬性須要裝配一個Hibernate的session工廠,HibernateTransactionManager的實現細節是它將事務管理的職責委託給org.hibernate.Transaction對象,然後者是從Hibernate Session中獲取到的。當事務成功完成時,HibernateTransactionManager將會調用Transaction對象的commit()方法,反之,將會調用rollback()方法。

2.1.3 Java持久化API事務(JPA)

Hibernate多年來一直是事實上的Java持久化標準,可是如今Java持久化API做爲真正的Java持久化標準進入你們的視野。若是你計劃使用JPA的話,那你須要使用Spring的JpaTransactionManager來處理事務。你須要在Spring中這樣配置JpaTransactionManager:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
	<property name="sessionFactory" ref="sessionFactory" />  
</bean>

JpaTransactionManager只須要裝配一個JPA實體管理工廠(javax.persistence.EntityManagerFactory接口的任意實現)。JpaTransactionManager將與由工廠所產生的JPA EntityManager合做來構建事務。

2.1.4 Java原生API事務

若是你沒有使用以上所述的事務管理,或者是跨越了多個事務管理源(好比兩個或者是多個不一樣的數據源),你就須要使用JtaTransactionManager:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">  
	<property name="transactionManagerName" value="java:/TransactionManager" />  
</bean>

JtaTransactionManager將事務管理的責任委託給javax.transaction.UserTransaction和javax.transaction.TransactionManager對象,其中事務成功完成經過UserTransaction.commit()方法提交,事務失敗經過UserTransaction.rollback()方法回滾。

2.2 基本事務屬性的定義

上面講到的事務管理器接口PlatformTransactionManager經過getTransaction(TransactionDefinition definition)方法來獲得事務,這個方法裏面的參數是TransactionDefinition類,這個類就定義了一些基本的事務屬性。 
那麼什麼是事務屬性呢?事務屬性能夠理解成事務的一些基本配置,描述了事務策略如何應用到方法上。事務屬性包含了5個方面,如圖所示:

而TransactionDefinition接口內容以下:

public interface TransactionDefinition {
	// 返回事務的傳播行爲  
	int getPropagationBehavior();
	// 返回事務的隔離級別,事務管理器根據它來控制另一個事務能夠看到本事務內的哪些數據  
	int getIsolationLevel();
	// 返回事務必須在多少秒內完成  
	int getTimeout();
	// 事務是否只讀,事務管理器可以根據這個返回值進行優化,確保事務是隻讀的  
	boolean isReadOnly();
}

咱們能夠發現TransactionDefinition正好用來定義事務屬性,下面詳細介紹一下各個事務屬性。

2.2.1 Spring 事務實現原理

在應用系統調用 事務聲明 的目標方法時,Spring Framework 默認使用 AOP 代理,在代碼運行時生成一個代理對象,根據事務配置信息,這個代理對象決定該聲明事務的目標方法是否由攔截器 TransactionInterceptor 來使用攔截,在 TransactionInterceptor 攔截時,會在目標方法開始執行以前建立並加入事務,並執行目標方法的邏輯, 最後根據執行狀況是否出現異常,利用抽象事務管理器 AbstractPlatformTransactionManager 操做數據源 DataSource 提交或回滾事務。

Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 兩種,以CglibAopProxy 爲例,對於CglibAopProxy,須要調用其內部類的DynamicAdvisedInterceptor 的 intercept 方法。對於 JdkDynamicAopProxy,則調用其 invoke 方法。

三:遇到的問題

3.1:若是隻是加了@Transactional註解,事務並不生效。

  • 事務註解就否開啓:
    • xml:<tx:annotation-driver transaction-manager="transactionManager">
    • java:@EnableTransactionManagement 開啓事務支持後。
  • 這個註解如果加載方法上,只能加在public的方法上,加到其餘如private上不會報錯,可是事務控制就不會生效。
  • mysql數據庫:myisam存儲引擎不支持事務,換成innodb

 

3.2 Spring事務中須要注意的問題

事務失效問題

在同一個代理對象內部,事務方法之間的直接嵌套調用,普通方法和事務方法之間的直接嵌套調用,都會形成事務異常!具體表現爲某些傳播行爲不生效或者直接事務控制不生效。

@Service
public class DemoService {  

    @Transaction
    public void transactionMethod1()  
    {  
        op1();
        op2();
        ...
    }  
    public void commonMethod(){  
        ...
        transactionMethod1();  //照顧基礎不牢的朋友,這裏至關於 this.transactionMethod1(); 
        ...  
    }  


    @Transaction
    public void transactionMethod2(){  
        ...
        this.transactionMethod3();
        ...
    }  

    @Transaction(propagation= Propagation.REQUIRES_NEW)
    public void transactionMethod3()  
    {  
        op3();
        ...
    }  
} 


上面代碼中,若是調用 DemoService 的 bean 對象的commonMethod ,則transactionMethod1裏定義的事務將不生效(好比op2發生錯誤時,並不會回滾op1的操做),bean 調用 transactionMethod2時,transactionMethod2時裏面調用的transactionMethod3也不會開啓新的事務。

爲何會這樣?
上面的實現機制中講到,AOP的實現都是經過動態代理來實現,而AOP限制了咱們只能在目標方法的開始和結束做爲切點作切入處理加強。當動態代理對象最終調用的原始對象的目標方法時,並不能干預到目標方法內的方法調用行爲,若是原始對象直接調用(用this.xxx方式)其餘同類方法時,實際調用的是原始對象自身的方法,而不是代理類對象加強後(增長事務控制後)的方法。此時Spring對方法事務的控制(包括事務的傳播行爲、事務的隔離級別等)徹底失效。

如何解決?
要想解決此類問題,主要都在於原始對象在調用對象內其餘方法時,不要使用this.xxx的方式直接調用,經過注入或者獲取代理對象的方式,使用代理對象調用須要調用的方法。下面列舉幾個解決方式:

1.注入自身,使用代理對象調用

@Service
public class DemoService {  

    @Autowired
    DemoService demoService;

    @Transaction
    public void transactionMethod1() {  
        op1();
        op2();
        ...
    }  

    public void commonMethod() {  
        ...
        //this.transactionMethod1() -> demoService.transactionMethod1()  
        demoService.transactionMethod1();  
        ...  
    } 
} 



2.使用AopContext,獲取當前代理對象

@Service
public class DemoService {

    @Transaction
    public void transactionMethod1()  
    {  
        op1();
        op2();
        ...
    }  

    public void commonMethod()  
    {  
        ...
        //this.transactionMethod1() -> ((DemoService)AopContext.currentProxy()).transactionMethod1();) 
        ((DemoService)AopContext.currentProxy()).transactionMethod1();
        ...  
    }  

    ...
} 


3.使用BeanFactory獲取代理對象(代碼略)
 

參考博客:

Spring事務配置的五種方式 : 

spring 事務傳播行爲實例分析

Spring事務管理(詳解+實例)

SpringBoot之事務處理機制

SpringBoot使用事務和AOP

相關文章
相關標籤/搜索