Spring Framework爲事務管理提供了一致的抽象,具備如下優點:php
傳統上,Java EE開發人員有兩種事務管理選擇:全局事務或本地事務,二者都有明顯的侷限性。html
全局事務:JTA、EJB 優勢:能夠多資源使用; 缺點:JTA API笨重、經過JNDI獲取資源。java
本地事務:本地事務是資源專用,好比:JDBC鏈接。 優勢:簡單易用; 缺點:不能多資源使用。spring
Spring解決了全局和本地事務的缺點。它使應用程序開發人員可以在任何環境中使用一致的編程模型。Spring Framework提供了聲明式和編程式事務管理。大多數用戶更喜歡聲明式事務管理,在大多數狀況下建議使用。大多數用戶更喜歡聲明式事務管理,在大多數狀況下建議使用。shell
經過編程式事務管理,開發人員可使用Spring Framework事務抽象,它能夠在任何底層事務基礎結構上運行。使用首選的聲明式模型,開發人員一般不多或根本不編寫與事務管理相關的代碼,所以不依賴於Spring Framework事務API或任何其餘事務API。express
Spring事務抽象的關鍵是事務策略的概念。事務策略由org.springframework.transaction.PlatformTransactionManager
接口定義:apache
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
複製代碼
getTransaction(..)
方法返回TransactionStatus
對象,具體取決於TransactionDefinition
參數。 返回的TransactionStatus
可能表示新事務,或者若是當前調用堆棧中存在匹配的事務,則能夠表示現有事務。 後一種狀況的含義是,與Java EE事務上下文同樣,TransactionStatus
與執行線程相關聯。編程
TransactionDefinition
用於描述事務的隔離級別、超時時間、是否只讀事務和事務傳播規則等控制事務具體行爲的事務屬性。oracle
public interface TransactionDefinition {
// 事務傳播
int getPropagationBehavior();
// 事務隔離級別
int getIsolationLevel();
// 事務超時事務
int getTimeout();
// 是否只讀
boolean isReadOnly();
// 事務名稱
String getName();
}
複製代碼
TransactionStatus
接口爲事務代碼提供了一種控制事務執行和查詢事務狀態的簡單方法。這些概念應該是熟悉的,由於它們對於全部事務API都是通用的:app
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
複製代碼
不管您是在Spring中選擇聲明式仍是程序化事務管理,定義正確的PlatformTransactionManager
實現都是絕對必要的。 您一般經過依賴注入來定義此實現。
PlatformTransactionManager
實現一般須要瞭解它們工做的環境:JDBC,JTA,Hibernate等。如下示例顯示瞭如何定義本地PlatformTransactionManager
實現。 (此示例適用於普通JDBC。)
首先,定義JDBC數據源。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
複製代碼
而後,相關的PlatformTransactionManager bean
定義將引用DataSource定義。
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
複製代碼
若是在Java EE容器中使用JTA,則使用經過JNDI得到的容器DataSource以及Spring的JtaTransactionManager。則配置以下:
<?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:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/> definitions here -->
</beans>
複製代碼
Spring爲不一樣的持久化框架提供了PlatformTransactionManager
接口的實現類。以下表所示:
org.springframework.orm.jpa.JpaTransactionManager
:使用JPA進行持久化,使用該事務管理器org.springframework.orm.hibernateX.HibernateTransactionManager
:使用Hibernate X.0(X可爲3,4,5)版本進行持久化時,使用該事務管理器org.springframework.jdbc.datasource.DataSourceTransactionManager
:使用Spring JDBC或MyBatis等基於DataSource數據源技術的持久化技術時,使用該事務管理器org.springframework.orm.jdo.JdoTransactionManager
:使用JDO進行持久化時,使用該事務管理器org.springframework.transaction.jta.JtaTransactionManager
:具備多個數據源的全局事務使用該事務管理器(無論採用何種持久化技術)本節描述直接或間接使用持久性API(如JDBC,Hibernate或JDO)的應用程序代碼如何確保正確建立,重用和清理這些資源。
High-level synchronization approach
首選方法是使用Spring基於最高級別模板的持久性集成API,或者將ORM API與事務感知工廠bean或代理一塊兒使用,以管理本地資源工廠。這些事務感知解決方案在內部處理資源建立和重用,清理,資源的可選事務同步以及異常映射。
所以,用戶數據訪問代碼沒必要解決這些任務,但能夠徹底專一於非樣板持久性邏輯。 一般,您使用本機ORM API或使用模板方法經過使用JdbcTemplate
進行JDBC訪問。
Low-level synchronization approach
Classes such as DataSourceUtils
(for JDBC), EntityManagerFactoryUtils
(for JPA), SessionFactoryUtils
(for Hibernate), PersistenceManagerFactoryUtils
(for JDO), and so on exist at a lower level. When you want the application code to deal directly with the resource types of the native persistence APIs, you use these classes to ensure that proper Spring Framework-managed instances are obtained, transactions are (optionally) synchronized, and exceptions that occur in the process are properly mapped to a consistent API.
For example, in the case of JDBC,
instead of the traditional JDBC approach of calling the getConnection()
method on the DataSource, you instead use Spring’s org.springframework.jdbc.datasource.DataSourceUtils
class as follows:
Connection conn = DataSourceUtils.getConnection(dataSource);
複製代碼
If an existing transaction already has a connection synchronized (linked) to it, that instance is returned. Otherwise, the method call triggers the creation of a new connection, which is (optionally) synchronized to any existing transaction, and made available for subsequent reuse in that same transaction. As mentioned, any SQLException
is wrapped in a Spring Framework CannotGetJdbcConnectionException
, one of the Spring Framework’s hierarchy of unchecked DataAccessExceptions. This approach gives you more information than can be obtained easily from the SQLException
, and ensures portability across databases, even across different persistence technologies.
This approach also works without Spring transaction management (transaction synchronization is optional), so you can use it whether or not you are using Spring for transaction management.
大多數Spring Framework用戶選擇聲明式事務管理。此選項對應用程序代碼的影響最小,所以最符合非侵入式輕量級容器的理想。
關於Spring Framework的聲明式事務支持,最重要的概念是經過AOP代理啓用此支持,而且事務切面由元數據(當前基於XML或基於註解)驅動。 AOP與事務元數據的組合產生一個AOP代理,該代理使用TransactionInterceptor
和適當的PlatformTransactionManager
實現來驅動圍繞方法調用的事務。
從概念上講,在事務代理上調用方法以下圖所示:
// the service interface that we want to make transactional
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);
}
複製代碼
// an implementation of the above interface
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();
}
}
複製代碼
假設FooService
接口的前兩個方法getFoo(String)
和getFoo(String,String)
必須在具備只讀語義的事務的上下文中執行,而且其餘方法,insertFoo(Foo)
和updateFoo( Foo)
,必須在具備讀寫語義的事務的上下文中執行。則配置文件以下:
<!-- 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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the transactional advice (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 PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
複製代碼
<tx:advice/>
標記的transaction-manager
屬性設置爲將驅動事務的PlatformTransactionManager bean的名稱,在本例中爲txManager bean。
<aop:config/>
定義確保txAdvice bean定義的事務切面在程序中的適當位置執行。 首先,定義一個切入點,該切入點與FooService接口(fooServiceOperation)中定義的任何操做的執行相匹配。 而後使用事務切面將切入點與txAdvice相關聯。 結果代表,在執行fooServiceOperation時,將運行txAdvice定義的切面。
驗證代碼以下:
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());
}
}
複製代碼
日誌信息以下:
<!-- the Spring container is starting up... -->
[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 [org.apache.commons.dbcp.PoolableConnection@a53de4] 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 [org.apache.commons.dbcp.PoolableConnection@a53de4]
[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)
複製代碼
向Spring Framework的事務基礎結構指示事務的工做將被回滾的推薦方法是從當前在事務上下文中執行的代碼中拋出異常。 Spring Framework的事務基礎結構代碼將捕獲任何未處理的異常,由於它會使調用堆棧冒泡,並肯定是否將事務標記爲回滾。
默認配置中,Spring Framework的事務基礎結構代碼僅在運行時未經檢查的異常狀況下標記用於回滾的事務; 也就是說,拋出的異常是RuntimeException
的實例或子類。從事務方法拋出的已檢查異常不會致使在默認配置中回滾。
您能夠準確配置哪些Exception類型標記用於回滾的事務,包括已檢查的異常。如下XML代碼段演示瞭如何爲已檢查的特定於應用程序的Exception類型配置回滾。
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
複製代碼
若是您不但願在拋出異常時回滾事務,也能夠指定「無回滾規則」。 如下示例告訴Spring Framework的事務基礎結構即便面對未處理的InstrumentNotFoundException也要提交事務。
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
複製代碼
當Spring Framework的事務基礎結構捕獲異常並參考配置的回滾規則以肯定是否將事務標記爲回滾時,最強匹配規則將獲勝。 所以,在如下配置的狀況下,除InstrumentNotFoundException
以外的任何異常都會致使後續事務的回滾。
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>
複製代碼
還能夠以編程方式指示所需的回滾。雖然很是簡單,但這個過程很是具備侵入性,而且將您的代碼緊密地耦合到Spring Framework的事務基礎結構中:
public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
複製代碼
<?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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="defaultServiceOperation" expression="execution(* x.y.service.*Service.*(..))"/>
<aop:pointcut id="noTxServiceOperation" expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
</aop:config>
<!-- this bean will be transactional (see 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>
複製代碼
除了基於XML的事務配置聲明方法以外,您還可使用基於註解的方法。
// 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配置來使bean實例成爲事務性的:
<!-- 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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.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 -->
<!-- a PlatformTransactionManager is still required -->
<tx:annotation-driven transaction-manager="txManager"/>
<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>
複製代碼
若是要鏈接的PlatformTransactionManager的bean名稱具備名稱transactionManager,則能夠省略<tx:annotation-driven />標記中的transaction-manager屬性。 若是要依賴注入的PlatformTransactionManager bean具備任何其餘名稱,則必須顯式使用transaction-manager屬性,如前面的示例所示。
若是使用基於Java的配置,則@EnableTransactionManagement批註提供等效支持。 只需將註解添加到@Configuration類便可。
Method visibility and @Transactional:
使用代理時,應僅將@Transactional註解應用於具備公共可見性的方法。 若是使用@Transactional註解對帶保護的,私有的或包可見的方法進行註解,則不會引起錯誤,但帶註解的方法不會顯示已配置的事務設置。 若是須要註解非公共方法,請考慮使用AspectJ。
能夠在接口定義,接口上的方法,類定義或類的公共方法以前放置@Transactional註解。 可是,僅僅存在@Transactional
註解不足以激活事務行爲。 @Transactional註解只是元數據,可由@Transactional
-aware的某些運行時基礎結構使用,而且可使用元數據來配置具備事務行爲的適當bean。
若是您但願自我調用也包含在事務中,請考慮使用AspectJ模式。 在這種狀況下,首先不會有代理; 相反,目標類將被編織(即,它的字節代碼將被修改),以便在任何類型的方法上將@Transactional
轉換爲運行時行爲。
大多數Spring應用程序只須要一個事務管理器,但在某些狀況下,您可能須要在單個應用程序中使用多個獨立的事務管理器。 @Transactional
註解的value
屬性可用於選擇性地指定要使用的PlatformTransactionManager
的標識。
public class TransactionalService {
@Transactional("order")
public void setSomething(String name) { ... }
@Transactional("account")
public void doSomething() { ... }
}
複製代碼
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="account"/>
</bean>
複製代碼
若是您發現許多不一樣方法在@Transactional上重複使用相同的屬性,那麼Spring的元註解支持容許您爲特定用例定義自定義快捷方式註解。例如,定義如下註解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}
複製代碼
應用自定義註解到TransactionalService
,以下:
public class TransactionalService {
@OrderTx
public void setSomething(String name) { ... }
@AccountTx
public void doSomething() { ... }
}
複製代碼
Spring經過事務傳播行爲控制當前的事務如何傳播到被嵌套調用的目標服務接口方法中。TransactionDefinition
接口中規定了7種類型的事務傳播行爲,以下:
PROPAGATION_REQUIRED
若是當前沒有事務,則新建一個事務;若是已經存在一個事務,則加入到這個事務中。這是最多見的選擇。PROPAGATION_SUPPORTS
支持當前事務。若是當前沒有事務,則以非事務方式執行。PROPAGATION_MANDATORY
使用當前事務。若是當前沒有事務,則拋出異常。PROPAGATION_REQUIRES_NEW
新建事務。若是當前存在事務,則把當前事務掛起。PROPAGATION_NOT_SUPPORTED
以非事務方式執行。若是當前存在事務,則拋出異常。PROPAGATION_NEVER
以非事務方式執行。若是當前存在事務,則拋出異常。PROPAGATION_NESTED
若是當前存在事務,則在嵌套事務內執行;若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。在Spring管理的事務中,請注意物理和邏輯事務之間的區別,以及傳播設置如何應用於此差別。
PROPAGATION_REQUIRED
當傳播設置爲PROPAGATION_REQUIRED
時,將爲應用該設置的每一個方法建立邏輯事務範圍。每一個這樣的邏輯事務範圍能夠單獨設置僅回滾狀態,外部事務範圍在邏輯上獨立於內部事務範圍。固然,在標準PROPAGATION_REQUIRED
行爲的狀況下,全部這些範圍將映射到同一物理事務。所以,內部事務範圍中的回滾標記確實會影響外部事務實際提交的機會。
可是,在內部事務做用域設置僅回滾標記的狀況下,外部事務還沒有決定回滾自己,所以回滾(由內部事務做用域靜默觸發)是意外的。此時拋出相應的UnexpectedRollbackException
。這是預期的行爲,所以事務的調用者永遠不會被誤導,假設在實際上沒有執行提交。所以,若是內部事務(外部調用者不知道)以靜默方式將事務標記爲僅回滾,則外部調用者仍會調用commit
。外部調用者須要接收UnexpectedRollbackException
以清楚地指示已執行回滾。
與PROPAGATION_REQUIRED
相比,PROPAGATION_REQUIRES_NEW對每一個受影響的事務範圍使用徹底獨立的事務。 在這種狀況下,底層物理事務是不一樣的,所以能夠獨立提交或回滾,外部事務不受內部事務的回滾狀態的影響。
PROPAGATION_NESTED
使用具備多個保存點的單個物理事務,它能夠回滾到該事務。 這種部分回滾容許內部事務做用域觸發其做用域的回滾,外部事務可以繼續物理事務,儘管已經回滾了一些操做。 此設置一般映射到JDBC保存點,所以僅適用於JDBC資源事務。
Suppose you want to execute both transactional and some basic profiling advice. How do you effect this in the context of <tx:annotation-driven/>
?
When you invoke the updateFoo(Foo)
method, you want to see the following actions:
Here is the code for a simple profiling aspect discussed above. The ordering of advice is controlled through the Ordered
interface.
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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.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" __order="200"__/>
<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>
複製代碼
The following example effects the same setup as above, but uses the purely XML declarative approach.
<?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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.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>
複製代碼
從Spring 4.2開始,事件的監聽器能夠綁定到事務的一個階段。 典型的例子是在事務成功完成時處理事件:當 當前事務的結果 對於監聽器實際上很重要時,這容許更靈活地使用事件。
註冊常規事件偵聽器是經過@EventListener
註解完成的。若是須要將其綁定到事務,請使用@TransactionalEventListener
。執行此操做時,默認狀況下,偵聽器將綁定到事務的提交階段。
@Service
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
return jdbcTemplate.queryForObject("SELECT * FROM foo where name = ?",
new Object[]{fooName}, Foo.class);
}
@Transactional
public void insertFoo(Foo foo) {
jdbcTemplate.update("INSERT INTO foo(name) VALUES (?)",
new Object[]{foo.getName()},
new int[]{Types.VARCHAR});
// 若是Foo沒有繼承ApplicationEvent, 則內部會包裝爲PayloadApplicationEvent。
// 發佈事件, 事務提交後, 記錄日誌, 或發送消息等操做。
applicationEventPublisher.publishEvent(foo);
//當事務提交後, 纔會真正的執行@TransactionalEventListener配置的Listener, 所以Listener拋異常, 方法返回失敗, 但事務不會回滾.
}
@Resource
private JdbcTemplate jdbcTemplate;
@Resource
private ApplicationEventPublisher applicationEventPublisher;
}
複製代碼
@Component
public class FooServiceListener {
@TransactionalEventListener
public void handler(PayloadApplicationEvent<FooService.Foo> creationEvent) {
FooService.Foo foo = creationEvent.getPayload();
System.out.println("======"+foo.getName());
System.out.println(1/0);
}
}
複製代碼
TransactionalEventListener
註解提供了一個階段屬性,該屬性容許自定義偵聽器應綁定到的事務的哪一個階段。 有效階段是BEFORE_COMMIT
,AFTER_COMMIT
(默認值),AFTER_ROLLBACK和AFTER_COMPLETION
,它們聚合事務完成(不管是提交仍是回滾)。 若是沒有正在運行的事務,則根本不調用偵聽器,由於咱們沒法遵照所需的語義。 可是,能夠經過將註解的fallbackExecution
屬性設置爲true
來覆蓋該行爲。