Spring_對事務的支持

前文:Spring_基於Spring的JDBC

 

事務應該在哪一層?

前文中,我們知道Spring的JDBC會幫我們管理事務。

在這種情況下,會出現什麼問題呢?

一些僞代碼:

public class AccountDaoImpl implements IAccountDao{
    /* 轉入 */
    public void transin(Long inId, BigDecimal value){
        // do work
    }
    /* 轉出 */
    public void transout(Long outId, BigDecimal value){
        // do work
    }
}
public class AccountServiceImpl implements IAccountService{
    /* 轉賬方法 */
    public void transfer(Long inId, Long outId, BigDecimal value){
        accountDao.transout(outId, value); // 轉出
        accountDao.transin(inId, value); // 轉入
    }
}

可以看到,在Service層中,轉賬方法是通過調用DAO層的轉出、轉入方法實現的。

但是,由於事務是在DAO層中進行管理的,若Service層在轉出後,系統出現異常,轉入代碼將無法繼續正常執行。

 正確的JDBC轉賬流程

1、進入Service層的轉賬方法時:獲取DataSoure對象,得到Connection對象。

2、關閉JDBC的事務自動提交:Connection對象.setAutoCommit(false);

3、將Connection對象綁定到當前線程中。

4、調用DAO層的方法,其Connection對象要從當前線程中獲取。

5、正常執行則提交事務;出現異常則回滾。

 

Spring的事務管理 

· 相關接口

TransactionDefinition

封裝事務的隔離級別,超時時間。是否爲只讀事務和事務的隔離級別,轉播規則等事務屬性。

可通過XML配置其具體信息。

PlatformTransactionManager

根據TeansactionDefinition提供的事務屬性,創建事務。

TransactionStatus 

封裝了事務的具體運行狀態。

如是否新開事務,是否已提交事務,可設置當前事務爲rollback-only等。

 · 事務管理的方式

Spring支持編程式事務管理和聲明式事務管理。

編程式事務管理

如使用Hibernate,我們需要在代碼中顯式調用beginTransaction()、commit()、rollback()等事務管理相關的方法,這就是編程式事務管理。

通過 Spring 提供的事務管理 API,我們可以在代碼中靈活控制事務的執行。在底層,Spring 仍然將事務操作委託給底層的持久化框架來執行。

這種方式事務和業務代碼耦合度太高

聲明式事務管理

Spring 的聲明式事務管理在底層是建立在 AOP 的基礎之上的。

其本質是對方法前後進行攔截,然後在目標方法開始之前創建或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。

這種方式侵入性小,把事務從業務代碼中抽離出來到配置文件中,提供維護性。

 

· 事務管理器

Spring並不直接管理事務,而是提供了多種事務管理器,將事務管理叫給Hibernate等持久化機制所提供的相關平臺框架的事務來實現。 

Spring事務管理器的接口是org.springframework.transaction.PlatformTransactionManager。

通過這個接口,Spring爲各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平臺自己的事情了。

spring提供的事務管理器

在使用Spring的事務時,需告訴Spring使用哪一個管理器。

其中DataSourceTransactionManager爲JDBC、MyBatis使用的事務管理器。

 

· PlatformTransactionManager中的方法

TransactionStatus getTransaction(TransactionDefinition definition)

根據TransactionDefinition事務定義信息,從事務環境返回一個已存在的事務,或創建一個新的事務。

並用TransactionStatus描述事務狀態。

void commit(TransactionStatus status)

根據事務狀態提交事務。

如事務狀態標識爲rollback-only,該方法執行回滾事務的操作。

void rollback(TransactionStatus status)

將事務回滾,當commit方法拋出異常時,rollback會被隱式調用

 

聲明式事務管理

基於AOP,使用事務管理器,對業務代碼進行事務增強。

· what :做什麼增強?

做事務管理器的增強。

告訴Spring使用的是哪一個具體的事務管理器。(如JDBC使用的是DataSourceTransactionManager。)

並對事務管理器做相應的配置。(如DataSourceTransactionManager需注入DataSource對象。)

如下例:

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

 

· When:在什麼時機增強?

事務應該是環繞增強。如下例: 

<tx:advice transaction-manager="txManager" id="txAdvice">
    <tx:attributes>
        <tx:method name="transfer"/>
    </tx:attributes>
</tx:advice>

<tx:advice>中配置transaction-manager屬性,該管理器做環繞增強。

<tx:attributes>元素中<tx:method>的屬性配置,可理解爲對TransactionDefinition對象做屬性配置以及一些額外的配置,以便事務管理器獲取事務,做回滾相關操作。

其name屬性值爲方法名,表示在對該方法做事務增強,可使用通配符*。

更多具體配置可參考文末。

 

· Where:在哪裏做增強?

通過AOP做相關配置,如下例:

<aop:config>
    <aop:pointcut id="txPoint" expression="execution(* com.hanaii.spring_tx.service.*Service.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
</aop:config>

對切入點進行配置後,我們還需要通過<aop:advisor>將advice與pointcut關聯起來。

這樣Spring才知道從哪個地方where,什麼時候when,做什麼樣what的切入。

 

<tx:method>中的屬性

 

· 事務傳播行爲

Spring在TransactionDefinition接口中規定了7種類型的事務傳播行爲,

它們規定了事務方法和事務方法發生嵌套調用時事務如何進行傳播。

 PROPAGATION_REQUIRED

如果當前沒有事務,就新建一個事務,

如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。

PROPAGATION_SUPPORTS

支持當前事務,如果當前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY

使用當前的事務,如果當前沒有事務,就拋出異常。

PROPAGATION_REQUIRES_NEW

新建事務,如果當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED

以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER

以非事務方式執行,如果當前存在事務,則拋出異常。

PROPAGATION_NESTED 

如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

 

 · CRUD通用的環繞增強

<tx:advice transaction-manager="txManager" id="txAdvice">
	<tx:attributes>
		<tx:method name="get*" read-only="true"/>
		<tx:method name="find*" read-only="true"/>
		<tx:method name="query*" read-only="true"/>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

 

· 使用註解配置事務

1、在Spring配置文件中配置註解解析器和事務管理器

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

<tx:annotation-driven transaction-manager="txManager"/>

 

2、使用@Transactional標籤進行標註

該註解源代碼:

public @interface Transactional {
    String value() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

該註解標註在類上時,其配置對該類所有方法生效。

同時,標註在方法時,可對某一方法進行局部的配置(如查詢方法配置read-only)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章