@Transactional註解及其支持類所提供的功能最低要求使用Java 5(Tiger)。 html
除了基於XML文件的聲明式事務配置外,你也能夠採用基於註解式的事務配置方法。直接在Java源代碼中聲明事務語義的作法讓事務聲明和將受其影響的代碼距離更近了,並且通常來講不會有不恰當的耦合的風險,由於,使用事務性的代碼幾乎老是被部署在事務環境中。 spring
下面的例子很好地演示了@Transactional註解的易用性,隨後解釋其中的細節。先看看其中的類定義: express
<!-- 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配置就可使它具備事務性的。以下: apache
<!-- 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"/> <!-- aPlatformTransactionManageris 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'來定義PlatformTransactionManagerbean的名字的話,你就能夠忽略<tx:annotation-driven/>標籤裏的'transaction-manager'屬性。 若是PlatformTransactionManagerbean你要經過其它名稱來注入的話,你必須用'transaction-manager'屬性來指定它,如上所示。 編程
方法的可見度和@Transactional oracle
@Transactional註解應該只被應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用@Transactional註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。 app
@Transactional註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅@Transactional註解的出現不足於開啓事務行爲,它僅僅 是一種元數據,可以被能夠識別@Transactional註解和上述的配置適當的具備事務行爲的beans所使用。上面的例子中,其實正是<tx:annotation-driven/>元素的出現 開啓 了事務行爲。 框架
Spring團隊的建議是你在具體的類(或類的方法)上使用@Transactional註解,而不要使用在類所要實現的任何接口上。你固然能夠在接口上使用@Transactional註解,可是這將只能當你設置了基於接口的代理時它才生效。由於註解是 不能繼承 的,這就意味着若是你正在使用基於類的代理時,那麼事務的設置將不能被基於類的代理所識別,並且對象也將不會被事務代理所包裝(將被確認爲嚴重的)。所以,請接受Spring團隊的建議而且在具體的類上使用@Transactional註解。 eclipse
當使用@Transactional風格的進行聲明式事務定義時,你能夠經過<tx:annotation-driven/>元素的 "proxy-target-class" 屬性值來控制是基於接口的仍是基於類的代理被建立。若是 "proxy-target-class" 屬值被設置爲 "true",那麼基於類的代理將起做用(這時須要CGLIB庫cglib.jar在CLASSPATH中)。若是 "proxy-target-class" 屬值被設置爲 "false" 或者這個屬性被省略,那麼標準的JDK基於接口的代理將起做用。 ide
在多數情形下,方法的事務設置將被優先執行。在下列狀況下,例如: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 } }
@Transactional註解是用來指定接口、類或方法必須擁有事務語義的元數據。 如:「當一個方法開始調用時就開啓一個新的只讀事務,並中止掉任何現存的事務」。 默認的@Transactional設置以下:
事務傳播設置是PROPAGATION_REQUIRED
事務隔離級別是ISOLATION_DEFAULT
事務是 讀/寫
事務超時默認是依賴於事務系統的,或者事務超時沒有被支持。
任何RuntimeException將觸發事務回滾,可是任何 checkedException將不觸發事務回滾
這些默認的設置固然也是能夠被改變的。@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的子類 | 一組異常類,遇到時 必須不 回滾 |
考慮這樣的狀況,你有一個類的實例,並且但願 同時插入事務性通知(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 aDataSourceand aPlatformTransactionManagerhere --> </beans>
上面配置的結果是建立了一個'fooService'bean,剖析方面和事務方面被 依照順序 施加其上。若是咱們但願剖析通知在目標方法執行以前 後於 事務通知執行,並且在目標方法執行以後 先於 事務通知,咱們能夠簡單地交換兩個通知bean的order值。
若是配置中包含更多的方面,它們將以一樣的方式受到影響。
經過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 theAnnotationTransactionAspectto 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進行「載入時織入」的討論。