天天看點

使用 @Transactional

9.5.6. 使用 @Transactional

注意

@Transactional 注解及其支援類所提供的功能最低要求使用Java 5(Tiger)。

除了基于XML檔案的聲明式事務配置外,你也可以采用基于注解式的事務配置方法。直接在Java源代碼中聲明事務語義的做法讓事務聲明和将受其影響的代碼距離更近了,而且一般來說不會有不恰當的耦合的風險,因為,使用事務性的代碼幾乎總是被部署在事務環境中。

下面的例子很好地示範了 @Transactional 注解的易用性,随後解釋其中的細節。先看看其中的類定義:

<!-- the service class that we want to make transactional -->

@Transactional

public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}當上述的POJO定義在Spring IoC容器裡時,上述bean執行個體僅僅通過一 行xml配置就可以使它具有事務性的。如下:

    <!-- from the file 'context.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:aop="http://www.springframework.org/schema/aop"

       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="

       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

  <!-- this is the service object that we want to make transactional -->

  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- enable the configuration of transactional behavior based on annotations -->

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

  <!-- a PlatformTransactionManager is still required -->

  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <!-- (this dependency is defined somewhere else) -->

    <property name="dataSource" ref="dataSource"/>

  </bean>

  <!-- other <bean/> definitions here -->

</beans>提示

實際上,如果你用 'transactionManager' 來定義 PlatformTransactionManager bean的名字的話,你就可以忽略 <tx:annotation-driven/> 标簽裡的 'transaction-manager' 屬性。 如果 PlatformTransactionManager bean你要通過其它名稱來注入的話,你必須用 'transaction-manager' 屬性來指定它,如上所示。

方法的可見度和 @Transactional

@Transactional 注解應該隻被應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯, 但是這個被注解的方法将不會展示已配置的事務設定。

@Transactional 注解可以被應用于接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 注解的出現不足于開啟事務行為,它僅僅 是一種中繼資料,能夠被可以識别 @Transactional 注解和上述的配置适當的具有事務行為的beans所使用。上面的例子中,其實正是 <tx:annotation-driven/>元素的出現 開啟 了事務行為。

Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實作的任何接口上。你當然可以在接口上使用 @Transactional 注解,但是這将隻能當你設定了基于接口的代理時它才生效。因為注解是 不能繼承 的,這就意味着如果你正在使用基于類的代理時,那麼事務的設定将不能被基于類的代理所識别,而且對象也将不會被事務代理所包裝(将被确認為嚴重的)。是以,請接受Spring團隊的建議并且在具體的類上使用 @Transactional 注解。

注意

當使用 @Transactional 風格的進行聲明式事務定義時,你可以通過 <tx:annotation-driven/> 元素的 "proxy-target-class" 屬性值來控制是基于接口的還是基于類的代理被建立。如果 "proxy-target-class" 屬值被設定為 "true",那麼基于類的代理将起作用(這時需要CGLIB庫cglib.jar在CLASSPATH中)。如果 "proxy-target-class" 屬值被設定為 "false" 或者這個屬性被省略,那麼标準的JDK基于接口的代理将起作用。

在多數情形下,方法的事務設定将被優先執行。在下列情況下,例如: DefaultFooService 類被注解為隻讀事務,但是,這個類中的 updateFoo(Foo) 方法的 @Transactional 注解的事務設定将優先于類級别注解的事務設定。

@Transactional(readOnly = true)

public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {

        // do something

    }

    // these settings have precedence for this method

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)

    public void updateFoo(Foo foo) {

        // do something

    }

}9.5.6.1. @Transactional 有關的設定

@Transactional 注解是用來指定接口、類或方法必須擁有事務語義的中繼資料。 如:“當一個方法開始調用時就開啟一個新的隻讀事務,并停止掉任何現存的事務”。 預設的 @Transactional 設定如下:

事務傳播設定是 PROPAGATION_REQUIRED

事務隔離級别是 ISOLATION_DEFAULT

事務是 讀/寫

事務逾時預設是依賴于事務系統的,或者事務逾時沒有被支援。

任何 RuntimeException 将觸發事務復原,但是任何 checked Exception 将不觸發事務復原

這些預設的設定當然也是可以被改變的。 @Transactional 注解的各種屬性設定總結如下:

表 9.2. @Transactional 注解的屬性

屬性 類型 描述

傳播性 枚舉型:Propagation  可選的傳播性設定

隔離性 枚舉型:Isolation  可選的隔離性級别(預設值:ISOLATION_DEFAULT)

隻讀性 布爾型 讀寫型事務 vs. 隻讀型事務

逾時 int型(以秒為機關) 事務逾時

復原異常類(rollbackFor) 一組 Class 類的執行個體,必須是Throwable 的子類 一組異常類,遇到時 必須 進行復原。預設情況下checked exceptions不進行復原,僅unchecked exceptions(即RuntimeException的子類)才進行事務復原。

復原異常類名(rollbackForClassname) 一組 Class 類的名字,必須是Throwable的子類 一組異常類名,遇到時 必須 進行復原

不復原異常類(noRollbackFor) 一組 Class 類的執行個體,必須是Throwable 的子類 一組異常類,遇到時 必須不 復原。

不復原異常類名(noRollbackForClassname) 一組 Class 類的名字,必須是Throwable 的子類 一組異常類,遇到時 必須不 復原

9.5.7. 插入事務操作

考慮這樣的情況,你有一個類的執行個體,而且希望 同時插入事務性通知(advice)和一些簡單的剖析(profiling)通知。那麼,在<tx:annotation-driven/>環境中該怎麼做?

我們調用 updateFoo(Foo) 方法時希望這樣:

配置的剖析切面(profiling aspect)開始啟動,

然後進入事務通知(根據配置建立一個新事務或加入一個已經存在的事務),

然後執行原始對象的方法,

然後事務送出(我們假定這裡一切正常),

最後剖析切面報告整個事務方法執行過程花了多少時間。

注意

這章不是專門講述AOP的任何細節(除了應用于事務方面的之外)。請參考 第 6 章 使用Spring進行面向切面程式設計(AOP) 章以獲得對各種AOP配置及其一般概念的詳細叙述。

這裡有一份簡單的剖析切面(profiling aspect)的代碼。(注意,通知的順序是由 Ordered 接口來控制的。要想了解更多細節,請參考 第 6.2.4.7 節 “通知(Advice)順序” 節。)

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;

import org.springframework.util.StopWatch;

import org.springframework.core.Ordered;

public class SimpleProfiler implements Ordered {

    private int order;

    // allows us to control the ordering of advice

    public int getOrder() {

        return this.order;

    }

    public void setOrder(int order) {

        this.order = order;

    }

    // this method is the around advice

    public Object profile(ProceedingJoinPoint call) throws Throwable {

        Object returnValue;

        StopWatch clock = new StopWatch(getClass().getName());

        try {

            clock.start(call.toShortString());

            returnValue = call.proceed();

        } finally {

            clock.stop();

            System.out.println(clock.prettyPrint());

        }

        return returnValue;

    }

}

這裡是幫助滿足我們上述要求的配置資料。

<?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:aop="http://www.springframework.org/schema/aop"

        xmlns:tx="http://www.springframework.org/schema/tx"

        xsi:schemaLocation="

   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this is the aspect -->

    <bean id="profiler" class="x.y.SimpleProfiler">

        <!-- execute before the transactional advice (hence the lower order number) -->

        <property name="order" value="1"/>

    </bean>

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

    <aop:config>

        <!-- this advice will execute around the transactional advice -->

        <aop:aspect id="profilingAspect" ref="profiler">

            <aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>

            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>

        </aop:aspect>

    </aop:config>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>

        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>

        <property name="username" value="scott"/>

        <property name="password" value="tiger"/>

    </bean>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

        <property name="dataSource" ref="dataSource"/>

    </bean>

</beans>上面配置的結果将獲得到一個擁有剖析和事務方面的 按那樣的順序 應用于它上面的 'fooService' bean。 許多附加的方面的配置将一起達到這樣的效果。

最後,下面的一些示例示範了使用純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:aop="http://www.springframework.org/schema/aop"

       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="

   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- the profiling advice -->

    <bean id="profiler" class="x.y.SimpleProfiler">

        <!-- execute before the transactional advice (hence the lower order number) -->

        <property name="order" value="1"/>

    </bean>

    <aop:config>

        <aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>

        <!-- will execute after the profiling advice (c.f. the order attribute) -->

        <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod"order="2"/>

        <!-- order value is higher than the profiling aspect -->

        <aop:aspect id="profilingAspect" ref="profiler">

            <aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>

            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>

        </aop:aspect>

    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="txManager">

        <tx:attributes>

            <tx:method name="get*" read-only="true"/>

            <tx:method name="*"/>

        </tx:attributes>

    </tx:advice>

    <!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->

</beans>上面配置的結果是建立了一個 'fooService' bean,剖析方面和事務方面被 依照順序 施加其上。如果我們希望剖析通知在目标方法執行之前 後于 事務通知執行,而且在目标方法執行之後 先于 事務通知,我們可以簡單地交換兩個通知bean的order值。

如果配置中包含更多的方面,它們将以同樣的方式受到影響。

9.5.8. 結合AspectJ使用 @Transactional

通過AspectJ切面,你也可以在Spring容器之外使用Spring架構的 @Transactional 功能。要使用這項功能你必須先給相應的類和方法加上 @Transactional注解,然後把 spring-aspects.jar 檔案中定義的 org.springframework.transaction.aspectj.AnnotationTransactionAspect 切面連接配接進(織入)你的應用。同樣,該切面必須配置一個事務管理器。你當然可以通過Spring架構容器來處理注入,但因為我們這裡關注于在Spring容器之外運作應用,我們将向你展示如何通過手動書寫代碼來完成。

注意

在我們繼續之前,你可能需要好好讀一下前面的第 9.5.6 節 “使用 @Transactional” 和 第 6 章 使用Spring進行面向切面程式設計(AOP) 兩章。

// construct an appropriate transaction manager

DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());

// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods

AnnotationTransactionAspect.aspectOf().setTransactionManager (txManager); 注意

使用此切面(aspect),你必須在 實作 類(和/或類裡的方法)、而 不是 類的任何所實作的接口上面進行注解。AspectJ遵循Java的接口上的注解 不被繼承 的規則。

類上的 @Transactional 注解指定了類裡的任何 public 方法執行的預設事務語義。

類裡的方法的 @Transactional 将覆寫掉類注解的預設事務語義(如何存在的話)。 public、protected和預設可見的方法可能都被注解。直接對 protected和預設可見的方法進行注解,讓這些方法在執行時去擷取所定義的事務劃分是唯一的途徑。

要把 AnnotationTransactionAspect 織入你的應用,你或者基于AspectJ建構你的應用(參考 AspectJ Development Guide),或者采取“載入時織入”(load-time weaving),參考 第 6.8.4 節 “在Spring應用中使用AspectJ Load-time weaving(LTW)” 獲得關于使用AspectJ進行“載入時織入”的讨論。

本文來自CSDN部落格,轉載請标明出處:http://blog.csdn.net/KOOK_OKKO/archive/2009/09/19/4565805.aspx

繼續閱讀