1. 程式人生 > >spring中事務配置的3種方式-2

spring中事務配置的3種方式-2

(3)、<tx:annotation-driven /> @Transactional 註解可以被應用於介面定義和介面方法、類定義和類的 public 方法上。 Spring團隊的建議是你在具體的類(或類的...今天配置spring 是碰到tx:annotation-driven is not bound 的問題,網上搜到,特意記錄下來。 http://blog.csdn.net/gabriel80/archive/2008/05/29/2492608.aspx ...

The prefix "tx" for element "tx:annotation-driven " is not bound 今天配置spring 是碰到tx:annotation-driven is not bound 的問題,這個錯誤的原因很簡單是...在springmvc配置檔案裡面寫預設的註解對映的支援的時候:<mvc:annotation-driven />The prefix "mvc" for element "mvc:annotation-driven" is not bound。 解決...

所以要去掉<mvc:annotation-driven />,去掉其預設配置, 否則會例項化兩個DefaultAnnotationHandlerMapping,並且不使用你配置的那個 DefaultAnnotationHandlerMapping. ... According to my spring mvc knowledge <mvc:annotation-driven/> and <context:annotation-config/> these both are used to detect the annotations like @...

While I will probably chose to not use any annotation driven dependency injection, somebody else may work primarily with annotations, and then fall back ... [B]How do I extract and add the needed parts of <mvc:annotation-driven/> to return JSON from the controller?[/B] Here are the pertinent parts ...

Spring MVC的一例低階錯誤:Element 'mvc:annotation-driven' must have no character or element information item [children], because the type's content type ... <mvc:annotation-driven/> 同樣可以達到以上的結果。 2.在controller中我們是這樣配置的: package com.yx.controller.annotation; import org.springframework....

9.5. 宣告式事務管理

大多數Spring使用者選擇宣告式事務管理。這是對應用程式碼影響最小的選擇,因此也最符合 非侵入式 輕量級容器的理念。

Spring的宣告式事務管理是通過Spring AOP實現的,因為事務方面的程式碼與Spring繫結並以一種樣板式風格使用,不過儘管如此,你一般並不需要理解AOP概念就可以有效地使用Spirng的宣告式事務管理。

從考慮EJB CMT和Spring宣告式事務管理的相似以及不同之處出發是很有益的。它們的基本方法是相似的:都可以指定事務管理到單獨的方法;如果需要可以在事務上下文呼叫setRollbackOnly() 方法。不同之處在於:

  • 不像EJB CMT繫結在JTA上,Spring宣告式事務管理可以在任何環境下使用。只需更改配置檔案,它就可以和JDBC、JDO、Hibernate或其他的事務機制一起工作。

  • Spring的宣告式事務管理可以被應用到任何類(以及那個類的例項)上,不僅僅是像EJB那樣的特殊類。

  • Spring提供了宣告式的回滾規則:EJB沒有對應的特性,我們將在下面討論。回滾可以宣告式的控制,不僅僅是程式設計式的。

  • Spring允許你通過AOP定製事務行為。例如,如果需要,你可以在事務回滾中插入定製的行為。你也可以增加任意的通知,就象事務通知一樣。使用EJB CMT,除了使用setRollbackOnly(),你沒有辦法能夠影響容器的事務管理。

  • Spring不提供高階應用伺服器提供的跨越遠端呼叫的事務上下文傳播。如果你需要這些特性,我們推薦你使用EJB。然而,不要輕易使用這些特性。通常我們並不希望事務跨越遠端呼叫。

TransactionProxyFactoryBean在哪兒?

Spring2.0及以後的版本中宣告式事務的配置與之前的版本有相當大的不同。主要差異在於不再需要配置TransactionProxyFactoryBean了。

Spring2.0之前的舊版本風格的配置仍然是有效的;你可以簡單地認為新的<tx:tags/>替你定義了TransactionProxyFactoryBean

回滾規則的概念比較重要:它使我們能夠指定什麼樣的異常(和throwable)將導致自動回滾。我們在配置檔案中宣告式地指定,無須在Java程式碼中。同時,我們仍舊可以通過呼叫TransactionStatussetRollbackOnly() 方法程式設計式地回滾當前事務。通常,我們定義一條規則,宣告MyApplicationException 必須總是導致事務回滾。這種方式帶來了顯著的好處,它使你的業務物件不必依賴於事務設施。典型的例子是你不必在程式碼中匯入Spring API,事務等。

對EJB來說,預設的行為是EJB容器在遇到 系統異常(通常指執行時異常)時自動回滾當前事務。EJB CMT遇到應用異常(例如,除了 java.rmi.RemoteException 外別的checked exception)時並不會自動回滾。預設式Spring處理宣告式事務管理的規則遵守EJB習慣(只在遇到unchecked exceptions時自動回滾),但通常定製這條規則會更有用。

9.5.1. 理解Spring的宣告式事務管理實現

本節的目的是消除與使用宣告式事務管理有關的神祕性。簡單點兒總是好的,這份參考文件只是告訴你給你的類加上@Transactional註解,在配置檔案中新增('<tx:annotation-driven/>')行,然後期望你理解整個過程是怎麼工作的。此節講述Spring的宣告式事務管理內部的工作機制,以幫助你在面對事務相關的問題時不至於誤入迷途,回朔到上游平靜的水域。

提示

閱讀Spring原始碼是理解清楚Spring事務支援的一個好方法。Spring的Javadoc提供的資訊豐富而完整。我們建議你在開發自己的Spring應用時把日誌級別設為'DEBUG'級,這樣你能更清楚地看到幕後發生的事。

在理解Spring的宣告式事務管理方面最重要的概念是:Spring的事務管理是通過AOP代理實現的。其中的事務通知由元資料(目前基於XML或註解)驅動。代理物件與事務元資料結合產生了一個AOP代理,它使用一個PlatformTransactionManager實現品配合TransactionInterceptor,在方法呼叫前後實施事務。

注意

儘管使用Spring宣告式事務管理不需要AOP(尤其是Spring AOP)的知識,但瞭解這些是很有幫助的。你可以在 第 6 章 使用Spring進行面向切面程式設計(AOP) 章找到關於Spring AOP的全部內容。

概念上來說,在事務代理上呼叫方法的工作過程看起來像這樣:

9.5.2. 第一個例子

請看下面的介面和它的實現。這個例子的意圖是介紹概念,使用 Foo Bar 這樣的名字只是為了讓你關注於事務的用法,而不是領域模型。

				<!-- 我們想做成事務性的服務介面 -->
            

package x.y.service;

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}
				<!-- 上述介面的一個實現 -->
            

package x.y.service;

public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        throw new UnsupportedOperationException();
    }

    public Foo getFoo(String fooName, String barName) {
        throw new UnsupportedOperationException();
    }

    public void insertFoo(Foo foo) {
        throw new UnsupportedOperationException();
    }

    public void updateFoo(Foo foo) {
        throw new UnsupportedOperationException();
    }
}

(對該例的目的來說,上例中實現類(DefaultFooService)的每個方法在其方法體中丟擲UnsupportedOperationException 的做法是恰當的,我們可以看到,事務被創建出來,響應UnsupportedOperationException 的丟擲,然後回滾。)

我們假定,FooService的前兩個方法(getFoo(String)getFoo(String, String))必須執行在只讀事務上下文中,其餘方法(insertFoo(Foo)updateFoo(Foo))必須執行在讀寫事務上下文中。

使用XML方式元資料的宣告式配置的話,你得這麼寫(不要想著一次全部理解,所有內容會在後面的章節詳細討論):

				<!-- '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">

  <!-- 這是我們將要配置並使它具有事務性的Service物件 -->
  
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  
  <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
  
  <tx:advice id="txAdvice" transaction-manager="txManager">
    <!-- the transactional semantics... -->
    
    <tx:attributes>
      <!-- all methods starting with 'get' are read-only -->
      
      <tx:method name="get*" read-only="true"/>
      <!-- other methods use the default transaction settings (see below) -->
      
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <!-- ensure that the above transactional advice runs for any execution
      of an operation defined by the FooService interface -->
      
  <aop:config>
    <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
  </aop:config>

  <!-- don't forget the DataSource -->
  
  <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>

  
  <!-- similarly, don't forget the (particular) PlatformTransactionManager -->
  
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>

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

我們來分析一下上面的配置。我們要把一個服務物件('fooService' bean)做成事務性的。我們想施加的事務語義封裝在<tx:advice/>定義中。<tx:advice/>把所有以'get' 開頭的方法看做執行在只讀事務上下文中,其餘的方法執行在預設語義的事務上下文中”。 其中的'transaction-manager' 屬性被設定為一個指向 PlatformTransactionManager bean的名字(這裡指 'txManager'),該bean將實際上實施事務管理。

提示

事實上,如果 PlatformTransactionManager bean的名字是 'transactionManager' 的話,你的事務通知(<tx:advice/>)中的'transaction-manager' 屬性可以忽略。否則你則需要像上例那樣明確指定。

配置中最後一段是 <aop:config/> 的定義,它確保由 'txAdvice' bean定義的事務通知在應用中合適的點被執行。首先我們定義了 一個切面,它匹配 FooService 介面定義的所有操作,我們把該切面叫做 'fooServiceOperation'。然後我們用一個通知器(advisor)把這個切面與'txAdvice' 繫結在一起,表示當 'fooServiceOperation' 執行時,'txAdvice' 定義的通知邏輯將被執行。

<aop:pointcut/> 元素定義是AspectJ的切面表示法,可參考Spring 2.0 第 6 章 使用Spring進行面向切面程式設計(AOP)章獲得更詳細的內容。

一個普遍性的需求是讓整個服務層成為事務性的。滿足該需求的最好方式是讓切面表示式匹配服務層的所有操作方法。例如:

<aop:config>
    <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
  </aop:config>

(這個例子中假定你所有的服務介面定義在 'x.y.service' 包中。你同樣可以參考第 6 章 使用Spring進行面向切面程式設計(AOP) 章獲得更詳細內容。)

現在,既然我們已經分析了整個配置,你可能會問了,“好吧,但是所有這些配置做了什麼?”。

上面的配置將為由 'fooService' 定義的bean建立一個代理物件,這個代理物件被裝配了事務通知,所以當它的相應方法被呼叫時,一個事務將被啟動、掛起、被標記為只讀,或者其它(根據該方法所配置的事務語義)。

我們來看看下面的例子,測試一下上面的配置。

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
        FooService fooService = (FooService) ctx.getBean("fooService");
        fooService.insertFoo (new Foo());
    }
}

執行上面程式的輸出結果看起來像這樣(注意為了清楚起見,Log4J的訊息和從 DefaultFooServiceinsertFoo(..) 方法丟擲的 UnsupportedOperationException 異常堆疊資訊被省略了)。

				<!-- Spring容器開始啟動... -->
				
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy
        for bean 'fooService' with 0 common interceptors and 1 specific interceptors
    <!-- the DefaultFooService is actually proxied -->
    
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]

    <!-- ... the insertFoo(..) method is now being invoked on the proxy -->
    

[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo
    <!-- the transactional advice kicks in here... -->
    
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection
        [[email protected]] for JDBC transaction

    <!-- the insertFoo(..) method from DefaultFooService throws an exception... -->
    
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should
        rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo
        due to throwable [java.lang.UnsupportedOperationException]

   <!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->
   
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection
        [[email protected]]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource

Exception in thread "main" java.lang.UnsupportedOperationException
	at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
   <!-- AOP infrastructure stack trace elements removed for clarity -->
   
	at $Proxy0.insertFoo(Unknown Source)
	at Boot.main(Boot.java:11)

9.5.3. 回滾

在前面的章節裡,概述瞭如何在你的應用裡為類特別是服務層的類指定事務性的基本方法。這一章將描述在一個簡單的宣告式配置中如何你才能控制事務的回滾。

一個容易的(和被推薦的)方法是在Spring框架的事務架構裡指出當context的事務裡的程式碼丟擲 Exception 時事務進行回滾。Spring框架的事務基礎架構程式碼將從呼叫的堆疊裡捕獲到任何未處理的 Exception,並將標識事務將回滾。

然而,請注意Spring框架的事務基礎架構程式碼將預設地 在丟擲執行時和unchecked exceptions時才標識事務回滾。 也就是說,當丟擲一個RuntimeException 或其子類例的例項時。(Errors 也一樣 - 預設地 - 標識事務回滾。)從事務方法中丟擲的Checked exceptions將 被標識進行事務回滾。

就是這些預設的設定;嚴格規定了哪些 Exception 型別將被標識進行事務回滾。 下面的XML配置片斷裡示範瞭如何配置一個checked,應用程式指定的Exception 型別來標識事務回滾。

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
	 <tx:method name="get*" read-only="false" rollback-for="NoProductInStockException"/>
	 <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

第二方法是在Spring框架的事務架構裡通過 程式設計式 方式指出一個事務將被回滾。 雖然這個非常簡單,但是這個方法對於Spring框架的事務架構來說,在你的程式碼是高入侵的和緊藕合的 下面的程式碼片斷裡示範了Spring框架管理事務的程式設計式回滾:

public void resolvePosition() {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmatically
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

強烈推薦你儘可能地使用宣告式事務回滾方法。 程式設計式方法的回滾對你來說是可見,如果你需要它你就可以使用,但是使用它就直接違反了在你的應用中使用一個純基於POJO的模型。

9.5.4. 為不同的bean配置不同的事務語義

現在我們考慮一下這樣的場景,你有許多服務物件,而且想為不同組的物件設定 完全不同 的事務語義。在Spring中,你可以通過定義各自特定的<aop:advisor/> 元素,每個advisor採用不同的 'pointcut''advice-ref' 屬性,來達到目的。

藉助於一個例子,我們假定你所有的服務層類定義在以 'x.y.service' 為根的包內。為了讓service包(或子包)下所有名字以'Service' 結尾的類的物件擁有預設的事務語義,你可以配置如下:

<?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">

<aop:config>

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

        <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>

</aop:config>

<!-- these two beans will be transactional... -->

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

<!-- ...and these two beans won't -->

    <bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) -->
    <bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in 'Service') -->

    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

下面的配置示例演示了兩個不同的bean擁有完全不同的事務配置。

<?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">

    <aop:config>

        <aop:pointcut id="defaultServiceOperation" expression="execution(* x.y.service.*Service.*(..))"/>
        <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
        
        <aop:pointcut id="noTxServiceOperation" expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
        <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>

    </aop:config>

    <!-- this bean will be transactional (c.f. the 'defaultServiceOperation' pointcut) -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this bean will also be transactional, but with totally different transactional settings -->
    <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

    <tx:advice id="defaultTxAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    
    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/>
        </tx:attributes>
    </tx:advice>

    <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

9.5.5. <tx:advice/> 有關的設定

這一節裡將描述通過 <tx:advice/> 標籤來指定不同的事務性設定。預設的 <tx:advice/> 設定如下:

  • 事務傳播設定是 REQUIRED

  • 隔離級別是 DEFAULT

  • 事務是 讀/寫

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

  • 任何 RuntimeException 將觸發事務回滾,但是任何 checked Exception 將不觸發事務回滾

這些預設的設定當然也是可以被改變的。 <tx:advice/> <tx:attributes/> 標籤裡的 <tx:method/> 各種屬性設定總結如下:

表 9.1. <tx:method/> 有關的設定

屬性 是否需要? 預設值 描述
name

與事務屬性關聯的方法名。萬用字元(*)可以用來指定一批關聯到相同的事務屬性的方法。 如:'get*''handle*''on*Event'等等。

propagation REQUIRED 事務傳播行為
isolation DEFAULT 事務隔離級別
timeout -1 事務超時的時間(以秒為單位)
read-only false 事務是否只讀?
rollback-for

將被觸發進行回滾的 Exception(s);以逗號分開。 如:'com.foo.MyBusinessException,ServletException'

no-rollback-for

被觸發進行回滾的 Exception(s);以逗號分開。 如:'com.foo.MyBusinessException,ServletException'

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容器之外執行應用,我們將向你展示如何通過手動書寫程式碼來完成。

// 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 將覆蓋掉類註解的預設事務語義(如何存在的話)。publicprotected和預設可見的方法可能都被註解。直接對protected和預設可見的方法進行註解,讓這些方法在執行時去獲取所定義的事務劃分是唯一的途徑。

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

http://blog.csdn.net/trwhoya/article/details/4535433

一、引入aop/tx名稱空間

<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/aop

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

    http://www.springframework.org/schema/tx

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

二、基於tx/aop配置

<!-- 事務管理器 -->

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory">

            <ref bean="sessionFactory"/>

        </property>

    </bean>

    <!-- 通過aop定義事務增強切面 -->

    <aop:config>

        <aop:pointcut id="serviceMethod" expression="execution(* com.cj.transaction.service.*.*(..))"/>

        <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>

    </aop:config>

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

        <tx:attributes>

            <tx:method name="find*" read-only="false"/>

            <tx:method name="add*" rollback-for="Exception"/>

        </tx:attributes>

    </tx:advice>

expression="exec