Spring.NET爲事務管理提供了一個持久化抽象(consistent abstraction ),其優勢以下:ios
這個章節分紅幾個小節,每一個小節都詳細介紹了Spring Framework的事務支持技術的技術或者價值。這個章節主要介紹的是一些關於如何最好地實現事務管理的討論。web
數據獲取技術是一個很普遍的概念,爲了實現事務管理在.NET BCL 中存在三種API,分別是ADO.NET, Enterprise Services, and System.Transactions。其餘的數據獲取技術,例如ORM(object relational mappers )和結果-集合映射庫(result-set mapping libraries)也有本身的事務管理API。也就是說,你的事務管理要使用特定的API來實現,所以你必須在開始編碼以前就要肯定你要用哪一種API。那麼有了新的需求須要改變你事務管理方式實現的時候,重構就會很麻煩。使用Spring 的事務管理API就可讓你使用一樣的API來實現各類不一樣的數據獲取技術。這樣,咱們主要經過修改簡單的配置文件或者少許的實現代碼,而不是去翻新整個項目來改變當前的事務管理實現。正則表達式
一步步地完成各類配置是你們普遍認爲最好的創建數據獲取方式。Martin Fowler的《企業應用架構模式》很好地介紹瞭如何獲取數據,以及這些方式如何在真實世界中普遍使用。其中,一個普遍使用的方式是在你的架構中引入一個數據獲取層(data access layer)。數據獲取層的做用範圍只限於數據獲取,僅關心如何在不一樣的數據獲取技術和數據庫之間提供輕便性。一個簡單的數據獲取層除了支持CRUD的數據獲取對象(data access objects,DAOs)以外,不會存在其餘的業務邏輯。業務邏輯主要包含在業務邏輯層中,在業務邏輯層中,業務邏輯會調用一個或多個DAO來實現終端用戶須要的一些功能。spring
爲了實現終端用戶「不全則無」的事務處理理念,事務上下文主要受到業務邏輯層控制(或者其餘更高級的層)。在這樣一個常見的場景中,一個很重要的實現細節是如何使DAO可以響應其餘層的「外部」事務。一個簡單的DAO可以實現各自的數據庫鏈接和事務管理,卻不支持多個DAO數據庫操做的事務,由於DAO僅關心他們自身的事務/資源(transaction/ resource)管理。所以,須要有一個可以將業務邏輯層的鏈接/事務(connection/transaction)傳送到DAO的方法。實現方式有不少,最簡單粗暴的方式就是直接把鏈接/事務對象看成參數傳到你的DAO中。還能夠將鏈接/事務對儲存在本地數據庫中。不管你使用哪一種方法,若是你使用的是ADO.NET,你都必須搭建一些基礎架構去實現。數據庫
等等,Enterprise Services 難道沒有任何解決這個問題的方法嗎,或者System.Transactions裏面的函數呢?答案是有的,但跟沒有同樣。在一個跨越多個DAO的事務上下文中,Enterprise Services讓你使用原始的ADO.NET API去完成事務管理。而Enterprise Services 的缺點是它老是經過微軟分佈式事務協調器(Microsoft Distributed Transaction Coordinator ,MS-DTC)的分佈式事務管理功能,對於大多數應用程序來講,僅僅使用這東西做爲事務管理方式性能要比本地ADO.NET 事務要低不少。express
使用System.Transactions 名稱空間下'using TransactionScope' 也存在類似的問題。TransactionScope 的目的是在一個using的聲明中定義一個事務範圍(transaction scope )。若是隻有一個事務,using 塊中的原始ADO.NET代碼接下來會變成一個基於本地ADO.NET的事務。 當其餘的事務被檢測到的時候,System.Transactions的神奇能力就會把本地 ADO.NET的事務轉化成分佈式事務。這種操做叫作Promotable Single Phase Enlistment(PSPE)。然而,須要注意的是若是你使用同一個數據庫鏈接字段來開啓另外一個IDbConnection 對象的數據庫鏈接,這樣作回致使本地事務轉化成分佈式事務。所以,若是你的DAO只是在執行他們本地的鏈接管理操做,它會莫名其妙地變成分佈式事務。爲了不這樣的問題產生,你必須傳一個鏈接對象到你的DAO。一樣要注意的是,不少數據庫還不支持PSPE,所以儘管你只用一個數據庫仍然使用的是分佈式事務管理方式。編程
接下來是關於使用聲明式事務管理。
Last but not least is the ability to use declarative transaction management. Not many topics in database transactionland give developers as much 'bang-for-the-buck' as declarative transactions since the noisy tedious bits of transactional API code in your application are pushed to the edges, usually in the form of class/method attributes. Only Enterprise Services offers this feature in the BCL. Spring fills the gap - it provides declarative transaction management if you are using local ADO.NET or System.Transactions (the most popular) or other data access technologies. Enterprise Services is not without it small warts as well, such as the need to separate your query/ retrieve operations from your create/update/delete operations if you want to use different isolation levels since declarative transaction metadata can only be applied at the class level. Nevertheless, all in all, Enterprise Services, in particular with the new 'Services Without Components' implementation for XP SP2/Server 2003, and hosted within the same process as your application code is as good as it gets out of the .NET box. Despite these positive points, it hasn't gained a significant mindshare in the development community.
Spring's transaction support aims to relieve these 'pain-points' using the data access technologies within the BCL - and for other third party data access technologies as well. It provides declarative transaction management with a configurable means to obtain transaction option metadata - out of the box attributes and XML within Spring's IoC configuration file are supported.數組
最後,Spring的事務支持讓你可以在一個事務中使用多種數據獲取技術——例如ADO.NET 和NHibernate。 網絡
通過這些婆婆媽媽的介紹以後,讓咱們進入正題。session
Spring事務管理的重要概念是事務策略(transaction strategy)。Spring.Transaction.IPlatformTransactionManager 接口定義了事務策略,以下所示:
public interface IPlatformTransactionManager { ITransactionStatus GetTransaction( ITransactionDefinition definition ); void Commit( ITransactionStatus transactionStatus ); void Rollback( ITransactionStatus transactionStatus ); }
這個是一個主要的「服務提供接口」(Service Provider Interface,SPI),儘管它能夠以編程的方式使用。注意它貫徹了Spring Framework的理念,IPlatformTransactionManager 是一個接口,所以能夠很容易地被更換或者修改。IPlatformTransactionManager的實現就和其餘的IoC容器中的對象同樣。主要提供了下面的實現:
封裝之下包含如下的API。對於AdoPlatformTransactionManager:Transaction.Begin(), Commit(), Rollback()。ServiceDomainPlatformTransactionManager 使用了'Services without Components' 的更新方式,所以你的對象不要繼承自ServicedComponent 或者須要直接調用Enterprise Services的 API: ServiceDomain.Enter(), Leave; ContextUtil.SetAbort()。TxScopePlatformTransactionManager 調用:new TransactionScope(); .Complete(), Dispose(), Transaction.Current.Rollback()。每一個事務管理器能夠配置他們的屬性來指定各自的數據獲取技術。能夠參考API文檔來得到更多的信息,可是這裏的例子應該能夠爲你提供一個很好的開始基礎。HibernatePlatformTransactionManager 會在下面的小節中詳細介紹。
GetTransaction(..) 會根據一個定義ITransactionDefinition 的參數返回一個ITransactionStatus 對象。返回的ITransactionStatus 對象表明一個新的或者現存的事務(若是當前的調用棧中存在對應的事務的話——
ITransactionDefinition 接口主要定義了:
這些配置反應了標準的事務概念。若是須要的話,請參考討論事務獨立程度的資源和其餘的關鍵事務概念,由於理解折現關鍵的概念對使用Spring Framework 或者其餘的事務管理解決方案很重要。
無論你是使用聲明式事務管理仍是可編程事務管理,定義正確的IPlatformTransactionManager 接口實現都是很重要的。在當前比較流行的Spring風格中,這些重要的定義都是經過依賴注入(Dependency Injection)來實現的。
IPlatformTransactionManager 接口的實現一般須要對他們工做環境,ADO.NET, NHibernate等等有必定程度上的瞭解。下面的例子展現了一個標準的基於ADO.NET 的IPlatformTransactionManager 實現。
首先,咱們必須定義一個Spring IDbProvider ,再經過引用IDbProvider,使用Spring的AdoPlatformTransactionManager。關於更多的IDbProvider 的信息,關注下一個章節。
<objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database"> <db:provider id="DbProvider" provider="SqlServer-1.1" connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/> <object id="TransactionManager" type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"> <property name="DbProvider" ref="DbProvider"/> </object> . . . other object definitions . . . </objects>
咱們也可使用基於System.Transactions的事務管理,就像下面展現的那樣:
<object id="TransactionManager" type="Spring.Data.Core.TxScopeTransactionManager, Spring.Data"> </object
在ORM事務管理章節也一樣定義了相似的HibernateTransactionManager。
須要注意的是,在這些例子中,應用程序的代碼都不須要改動,由於在使用這些戰略模式(strategy pattern)的時候,依賴注入式一個完美的夥伴。咱們如今能夠僅僅經過修改配置文件來改變整個事務管理,儘管這個改變意味着從局部到全局,或者相反,從全局到局部。
應用程序代碼是如何經過不一樣的事務管理器參與資源(例如,鏈接/事務/session)的生成/重用/清理呢?有兩個方法—一個高級方法和一個低級方法。
比較推薦的方式是Spring的高級持久化集成API。這些API不須要替換本地的API,可是會在內部處理生成,重用,清理和可選的事務同步(例如事件通知),以及異常的映射等等。所以用戶在使用數據獲取的代碼的時候就不須要關係這些實現細節,能夠專一於非模板化的持久化邏輯。總的來講,一樣的控制反轉的方式可使用在全部的持久化API上面。這些API包含回調方法或者委託來通知相應的資源已經可使用。例如,a DbCommand with its Connection and Transaction properties set based on the transaction option metadata. 這些類使用模板機制,例如AdoTemplate 和 HibernateTemplate。Convenient 'one-liner' helper methods in these template classes build upon the core callback/IoC design by providing specific implementations of the callback interface.(真心看不懂到底想表達什麼,講真,這個文檔挺囉嗦的,並且寫得對外國人很不友好。)
能夠用一個工具類來得到一個可以感知事務調用上下文調用的「鏈接/事務」對。ConnectionUtils 包含靜態方法GetConnectionTxPair(IDbProvider provider)來實現這個功能。
public class LowLevelIntegration { // Spring's IDbProvider abstraction private IDbProvider dbProvider; public IDbProvider dbProvider { set { dbProvider = value; } } public void DoWork() { ConnectionTxPair connTxPair = ConnectionUtils.GetConnectionTxPair(dbProvider); //Use some data access library that allows you to pass in the transaction DbWrapper dbWrapper = new DbWrapper(); string cmdText = ... // some command text dbWrapper.ExecuteNonQuery(cmdText, connTxPair.Transaction); } }
大多Spring 的用戶會選擇聲明式事務管理方式。由於這種方式對應用程序的代碼影響最小,因此是非侵入式(non-invasive)輕量級容器的實現目標一致。
Spring 的聲明式事務管理能夠和Spring的AOP一塊兒使用,然而Spring的事務切面的使用比較模板化,所以咱們沒必要太須要知道AOP的概念就可使用相關的事務管理功能了。
爲了在每一個單獨方法的層面上肯定事務的行爲。在每一個事務上下文中均可以調用SetRollbackOnly() 方法來標記這個事務的回滾。一些Spring聲明式事務的重點主要包括:
回滾規則的概念是很重要的:它讓咱們可以定義何種錯誤會形成自動回滾。咱們能夠在配置文件中定義而不是在業務代碼中。所以,儘管你能夠在ITransactionStatus對象中調用SetRollbackOnly() 方法來將如今的事務回滾,然而在更多的狀況下,你能夠定義某種規則來讓你的某種應用程序錯誤必定會致使回滾。這種作法的一個優勢就是你的業務對象不要依賴事務功能基礎模塊。例如,不須要引入任何的Spring 事務API或者其餘的Spring API。然而若是要經過編程式地回滾事務,就須要要調用框架方法:
TransactionInterceptor.CurrentTransactionStatus.SetRollbackOnly()
除非你知道它全部工做原理,否則的話,只是簡單地告訴你用[Transaction]特性去標記你的類,或者是在配置文件中添加<tx:attribute-driven/>字段就想讓你瞭解整個事務管理是如何控制,那是遠遠不夠的。這一個小節主要解釋Spring Framework的聲明式事務管理架構如何工做。
掌握Spring Framework的聲明式事務管理最重要的是要明白它支持AOP代理,而且事務通知是使用元數據驅動(XML或者特性標籤)。這種方式使用一個事務攔截器和一個對應的IPlatformTransactionManager 實現來完成特定方法調用時候的環繞通知。
理論上,在一個事務代理中調用一個方法是這樣的:
仔細思考一下下面的接口,這麼作是爲了傳達相應的概念給你而不是讓你糾結於領域細節。
ITestObjectManager 是一個簡單的實現,這個實現包含兩個DAO的調用。很明顯從業務邏輯層層面來講這個service太簡單了,它的接口以下:
public interface ITestObjectManager { void SaveTwoTestObjects(TestObject to1, TestObject to2); void DeleteTwoTestObjects(string name1, string name2); }
ITestObjectManager 的實現以下:
public class TestObjectManager : ITestObjectManager { // Fields/Properties ommited [Transaction] public void SaveTwoTestObjects(TestObject to1, TestObject to2) { TestObjectDao.Create(to1.Name, to1.Age); TestObjectDao.Create(to2.Name, to1.Age); } [Transaction] public void DeleteTwoTestObjects(string name1, string name2) { TestObjectDao.Delete(name1); TestObjectDao.Delete(name2); } }
要注意的是方法上的事務標籤。其餘的配置選項例如isolation也能夠在這個標籤中設置,可是例子中的特性用的是默認配置選項。然而要注意的是一個特性標籤是不足以控制整個事務行爲的——特性標籤只是一個簡單的元數據,能被事務特性感知的東西獲取以配置相應的對象來完成整個事務行爲。
TestObjectDao 屬性有一些基礎的增刪改查和找到領域模型TestObject的功能。TestObject 有一些基本的屬性例如姓名和年齡。
public interface ITestObjectDao { void Create(string name, int age); void Update(TestObject to); void Delete(string name); TestObject FindByName(string name); IList FindAll(); }
增長和刪除方法的實現以下。須要注意的是這些實現使用的是AdoTemplate類(會在後文詳細討論)
public class TestObjectDao : AdoDaoSupport, ITestObjectDao { public void Create(string name, int age) { AdoTemplate.ExecuteNonQuery(CommandType.Text, String.Format("insert into TestObjects(Age, Name) VALUES ({0}, '{1}')", age, name)); } public void Delete(string name) { AdoTemplate.ExecuteNonQuery(CommandType.Text, String.Format("delete from TestObjects where Name = '{0}'", name)); } }
TestObjectManager的DAO使用了依賴注入技術。客戶端代碼,在這個例子中直接訪問Spring IoC容器以得到ITestObjectManager的實現,會獲得一個使用特性標籤配置的事務代理。須要注意的是ITestObjectManager 多是某些更高層中的對象的依賴注入,例如一個網絡服務層。
客戶端的調用代碼以下
IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/ autoDeclarativeServices.xml"); ITestObjectManager mgr = ctx["testObjectManager"] as ITestObjectManager; TestObject to1 = new TestObject(); to1.Name = "Jack"; to1.Age = 7; TestObject to2 = new TestObject(); to2.Name = "Jill"; to2.Age = 8; mgr.SaveTwoTestObjects(to1, to2); mgr.DeleteTwoTestObjects("Jack", "Jill");
DAO的對象定義和管理類的配置以下
<objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database"> <db:provider id="DbProvider" provider="SqlServer-1.1" connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/> <object id="transactionManager" type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"> <property name="DbProvider" ref="DbProvider"/> </object> <object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data"> <property name="DbProvider" ref="DbProvider"/> </object> <object id="testObjectDao" type="Spring.Data.TestObjectDao, Spring.Data.Integration.Tests"> <property name="AdoTemplate" ref="adoTemplate"/> </object> <!-- The object that performs multiple data access operations --> <object id="testObjectManager" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> </objects>
這是一個標準的Spring配置而且提供了靈活的鏈接字符串的參數化和DAO實體的便利轉換。
如下小節會展現如何使用Spring的事務名稱空間來配置聲明式事務
Spring提供了一個能夠自定義的XML schema來簡化聲明式事務管理的配置。若是你像使用基於特性的事務管理,你要先爲你的事務名稱空間註冊對應的名稱空間解析器。這個能夠在應用程序配置文件中定義,以下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="spring"> <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" /> <!-- other spring config sections like context, typeAliases, etc not shown for brevity --> </sectionGroup> </configSections> <spring> <parsers> <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /> <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" /> <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" /> </parsers> </spring> </configSections>
除了使用前面介紹的XML配置文件(declarativeServices.xml )以外,你能夠可使用下面的方式。
<objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.net/tx" xmlns:db="http://www.springframework.net/database" xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/ spring-objects.xsd http://www.springframework.net/schema/tx http://www.springframework.net/schema/tx/spring-tx-1.1.xsd" http://www.springframework.net/schema/db http://www.springframework.net/schema/db/spring database.xsd"> <db:provider id="DbProvider" provider="SqlServer-1.1" connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/> <object id="transactionManager" type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"> <property name="DbProvider" ref="DbProvider"/> </object> <object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data"> <property name="DbProvider" ref="DbProvider"/> </object> <object id="testObjectDao" type="Spring.Data.TestObjectDao, Spring.Data.Integration.Tests"> <property name="AdoTemplate" ref="adoTemplate"/> </object> <!-- The object that performs multiple data access operations --> <object id="testObjectManager" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> <tx:attribute-driven transaction-manager="transactionManager"/> </objects>
<tx:attribute-driven/>標籤的各類配置選項總結以下:
表格: <tx:annotation-driven/> 設置
屬性 | 是否必須 | 默認值 | 介紹 |
transaction-manager | 否 | transactionManager | 要使用的事務管理對象的名稱。僅僅在事務管理對象的名稱不是transactionManager的時候是必須指定的,和上文中的例子同樣。 |
proxy-target-type | 否 | 控制應用[Transaction]特性的對象上生成的事務代理的類型。若是這個特性設置成true,這個基於類定義的代理就會被生成。代理繼承自目標類,然而對目標的調用仍然經過是代理對象。若是這個特性設置成false或者置空,那麼一個基於代理的而且你只能把代理設置到接口的實現上。(能夠參見「代理機制」得到更多的不一樣類型的代理的詳細例子) (proxy inherits from target class, however calls are still delegated to target object via composition. This allows for casting to base class. If "proxy-targettype" is "false" or if the attribute is omitted, then a pure composition based proxy is created and you can only cast the proxy to implemented interfaces. (See the section entitled Section 13.6, 「Proxying mechanisms」 for a detailed examination of the different proxy types.) |
|
order | 否 | 定義將要應用[Transaction]特性的對象上的通知順序。更多關於AOP通知順序的信息能夠在AOP章節中查找(查看「通知順序」章節)。要注意若是沒有定義任何順序,就會根據AOP子系統的默認順序進行。 |
你也能夠經過使用<tx:advice>來定義你想要應用的事務語法。和使用事務特性的狀況不同,你能夠定義例如propagation 或者isolation 程度和methods for which that metadata applies external to the code 。<tx:advice>在解析的時候會生成ITransactionAttributeSource 的實現實例。若是在上面的例子中使用<tx:advice>而不是<tx:attribute-driven/>的話,就好比這樣
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="Save*"/> <tx:method name="Delete*"/> </tx:attributes> </tx:advice>
這個意味着全部以Save或者Delete開頭的方法就會和事務元數據的默認配置關聯。默認數據會在下文列出來。
下面是一個使用<tx:method/>的例子:
<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> object below) --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 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>
<tx:advice/> 的含義是「全部的以‘Get’開頭的方法會在一個只讀事務上下文中執行,而且其餘全部方法將會用默認的事務語法執行」。標籤中的「'transactionmanager' 」屬性被設置成了PlatformTransactionManager 對象的名稱,這個對象會驅動整個事務(在'transactionManager' 對象的例子中)。
你也可使用<aop:advisor>把一個切入點和上文定義的通知綁定起來,就像這樣:
<object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> <property name="pattern" value="Spring.TxQuickStart.Services.*"/> </object> <aop:config> <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/> </aop:config>
假設服務層類TestObjectManager是在Spring.TxQuickStart.Services名稱空間下。<aop:config/> 保證這個被txAdvice定義的事務通知可以在正確的時間點執行。首先咱們定義了一個切入點匹配全部Spring.TxQuickStart.Services 名稱空間下的類(你能夠經過正則表達式來更詳細地配置)。而後咱們使用一個通知器將這個切入點和txAdvice關聯起來。這個例子中,配置結果意味着執行'SaveTwoTestObjects' and 'DeleteTwoTestObject'的時候,'txAdvice' 通知就會被執行。
各類各樣的事務配置都能使用<tx:advice/>標籤來配置。默認的<tx:advice/>在下文中列出,與你使用事務特性的時候是同樣的。
這些默認設置都是能夠修改的,<tx:advice/> 和<tx:attributes/>中須要的<tx:method/>標籤都總結以下:
表格: <tx:method/> 的設置
屬性 |
是否必須 |
默認值 |
描述 |
name |
是 |
|
事務屬性關聯的方法的名稱。通配符(*)能夠用於將相同的事務特性配置與一組方法關聯; 例如名稱相似’Get*’,’Handle*’,’On*Event’等等的方法。 |
propagation |
否 |
Required |
事務傳播行爲 |
isolation |
否 |
ReadCommitted |
事務隔離等級 |
timeout |
否 |
-1 |
事務超時(秒) |
read-only |
否 |
fale |
事務只讀屬性 |
EnterpriseServicesInteropOption |
否 |
None |
|
rollback-for |
否 |
|
觸發回滾的異常,使用逗號分隔,例如,'MyProduct.MyBusinessException,ValidationException' |
no-rollback-for |
否 |
|
不會觸發回滾的異常,使用逗號分隔,例如,'MyProduct.MyBusinessException,ValidationException' |
事務特性是指定類或者方法包含的特定事務語法的元數據 ,默認的事務特性設置是這樣的:
默認配置理所固然是能夠修改的,事務特性的各類性質總結以下:
屬性 | 類型 | 描述 |
TransactionPropagation | 枚舉,Spring.Transaction.TransactionPropagation |
propagation設置. (Required, Supports, Mandatory, RequiresNew, NotSupported, Never, Nested) |
Isolation | System.Data.IsolationLevel | isolation level設置 |
ReadOnly | 布爾 | 是可讀可寫的仍是隻讀的事務 |
EnterpriseServicesInteropOption | 枚舉,System.Transactions.EnterpriseServicesInteropOption | 可選的和COM+事務的互操做配置(僅適用於.NET 2.0 和 TxScopeTransactionManager ) |
Timeout | 整型(秒) | 事務超時設置 |
RollbackFor | type對象數組 | 一個必定會形成回滾的異常對象數組 |
NoRollbackFor | type對象數組 | 一個必定不會形成回滾的異常對象數組 |
須要注意的是,當一個真實的嵌套事務產生的時候,若是把TransactionPropagation 設置成Nested的話,例如不是在使用Nested propagation 而是在沒有任何nested調用的狀況下,會拋出NestedTransactionNotSupportedException 異常。這個問題會在可以支持nested事務的SqlServer和Oracle 的Spring 1.2的發佈版中解決。還需注意的是,isolation稱固定的改變仍然會在Spring1.2發佈版中解決。
Note that setting the TransactionPropagation to Nested will throw a NestedTransactionNotSupportedException in a case where an actual nested transaction occurs, i.e. not in the case of applying the Nested propagation but in fact no nested calls are made. This will be fixed for the Spring 1.2 release for SqlServer and Oracle which support nested transactions. Also note, that changing of isolation levels on a per-method basis is also scheduled for the Spring 1.2 release since it requires detailed command text metadata for each dbprovider. Please check the forums for news on when this feature will be introduced into the nightly builds.
If you specify an exception type for 'NoRollbackFor' the action taken is to commit the work that has been done in the database up to the point where the exception occurred. The exception is still propagated out to the calling code.
The ReadOnly boolean is a hint to the data access technology to enable read-only optimizations. This currently has no effect in Spring's ADO.NET framework. If you would like to enable read-only optimizations in ADO.NET this is generally done via the 'Mode=Read' or 'Mode=Read-Only" options in the connection string. Check your database provider for more information. In the case of NHibernate the flush mode is set to Never when a new Session is created for the transaction.
Throwing exceptions to indicate failure and assuming success is an easier and less invasive programming model than performing the same task Programatically - ContextUtil.MyTransactionVote or TransactionScope.Complete. The rollback options are a means to influence the outcome of the transaction based on the exception type which adds an extra degree of flexibility.
Having any exception trigger a rollback has similar behavior as applying the AutoComplete attribute available when using .NET Enterprise Services. The difference with AutoComplete is that using AutoComplete is also coupled to the lifetime of the ServicedComponent since it sets ContextUtil.DeactivateOnReturn to true. For a stateless DAO layer this is not an issue but it could be in other scenarios. Spring's transactional aspect does not affect the lifetime of your object.
若是你選擇在聲明式事務管理中不適用事務名稱空間,你可使用「低級」對象定義來配置聲明式事務管理。
if you choose not to use the transaction namespace for declarative transaction management then you can use 'lower level' object definitions to configure declarative transactions. The use of Spring's autoproxy functionality defines criteria to select a collection of objects to create a transactional AOP proxy. There are two AutoProxy classes that you can use, ObjectNameAutoProxyCreator and DefaultAdvisorAutoProxyCreator. If you are using the new transaction namespace support you do not need to configure these objects as a DefaultAdvisorAutoProxyCreator is created 'under the covers' while parsing the transaction namespace elements
The ObjectNameAutoProxyCreator is useful when you would like to create transactional proxies for many objects. The definitions for the TransactionInterceptor and associated attributes is done once. When you add new objects to your configuration file that need to be proxies you only need to add them to the list of object referenced in the ObjectNameAutoProxyCreator. Here is an example showing its use. Look in the section that use ProxyFactoryObject for the declaration of the transactionInterceptor.
<object name="autoProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> <property name="InterceptorNames" value="transactionInterceptor"/> <property name="ObjectNames"> <list> <idref local="testObjectManager"/> </list> </property> </object>
This is not longer a common way to configure declarative transactions but is discussed in the "Classic Spring" appendiex here.
Spring provides two means of programmatic transaction management:
Using the TransactionTemplate
Using a IPlatformTransactionManager
implementation directly
These are located in the Spring.Transaction.Support namespace. If you are going to use programmatic transaction management, the Spring team generally recommends the first approach (i.e. Using the TransactionTemplate
)
The TransactionTemplate adopts the same approach as other Spring templates such as AdoTemplate
and HibernateTemplate
. It uses a callback approach, to free application code from having to do the boilerplate acquisition and release of resources, and results in code that is intention driven, in that the code that is written focuses solely on what the developer wants to do. Granted that the using construct of System.Transaction alleviates much of this. One key difference with the approach taken with the TransactionTemplate is that a commit is assumed - throwing an exception triggers a rollback instead of using the TransactionScope API to commit or rollback. This also allows for the use of rollback rules, that is a commit can still occur for exceptions of certain types.
Application code that must execute in a transaction context looks like this. You, as an application developer, will write a ITransactionCallback implementation (typically expressed as an anonymous delegate) that will contain all of the code that you need to have execute in the context of a transaction. You will then pass an instance of your custom ITransactionCallback to the Execute(..) method exposed on the TransactionTemplate. Note that the ITransactionCallback
can be used to return a value:
public class SimpleService : IService { private TransactionTemplate transactionTemplate; public SimpleService(IPlatformTransactionManager transactionManager) { AssertUtils.ArgumentNotNull(transactionManager, "transactionManager"); transactionTemplate = new TransactionTemplate(transactionManager); } public object SomeServiceMethod() { return tt.Execute(delegate { UpdateOperation(userId); return ResultOfUpdateOperation2(); }); } }
This code example is specific to .NET 2.0 since it uses anonymous delegates, which provides a particularly elegant means to invoke a callback function as local variables can be referred to inside the delegate, i.e. userId. In this case the ITransactionStatus was not exposed in the delegate (delegate can infer the signature to use), but one could also obtain a reference to the ITransactionStatus instance and set the RollbackOnly property to trigger a rollback - or alternatively throw an exception. This is shown below
tt.Execute(delegate(ITransactionStatus status) { try { UpdateOperation1(); UpdateOperation2(); } catch (SomeBusinessException ex) { status.RollbackOnly = true; } return null; });
If you are using .NET 1.1 then you should provide a normal delegate reference or an instance of a class that implements the ITransactionCallback interface. This is shown below
tt.Execute(new TransactionRollbackTxCallback(amount)); public class TransactionRollbackTxCallback : ITransactionCallback { private decimal amount; public TransactionRollbackTxCallback(decimal amount) { this.amount = amount } public object DoInTransaction(ITransactionStatus status) { adoTemplate.ExecuteNonQuery(CommandType.Text, "insert into dbo.Debits (DebitAmount) VALUES (@amount)", "amount", DbType.Decimal, 0,555); // decide you need to rollback... status.RollbackOnly = true; return null; } }
Transaction settings such as the propagation mode, the isolation level, the timeout, and so forth can be set on the TransactionTemplate either programmatically or in configuration. TransactionTemplate instances by default have the default transactional settings. Find below an example of programmatically customizing the transactional settings for a specific TransactionTemplate.
public class SimpleService : IService { private TransactionTemplate transactionTemplate; public SimpleService(IPlatformTransactionManager transactionManager) { AssertUtils.ArgumentNotNull(transactionManager, "transactionManager"); transactionTemplate = new TransactionTemplate(transactionManager); // the transaction settings can be set here explicitly if so desired transactionTemplate.TransactionIsolationLevel = IsolationLevel.ReadUncommitted; transactionTemplate.TransactionTimeout = 30; // and so forth... } . . . }
Find below an example of defining a TransactionTemplate with some custom transactional settings, using Spring XML configuration. The 'sharedTransactionTemplate' can then be injected into as many services as are required.
<object id="sharedTransactionTemplate" type="Spring.Transaction.Support.TransactionTemplate, Spring.Data"> <property name="TransactionIsolationLevel" value="IsolationLevel.ReadUncommitted"/> <property name="TransactionTimeout" value="30"/> </object>
You can also use the PlatformTransactionManager directly to manage your transaction. Simply pass the implementation of the PlatformTransactionManager you're using to your object via a object reference through standard Dependency Injection techniques. Then, using the TransactionDefinition and ITransactionStatus objects, you can initiate transactions, rollback and commit.
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.PropagationBehavior = TransactionPropagation.Required; ITransactionStatus status = transactionManager.GetTransaction(def); try { // execute your business logic here } catch (Exception e) { transactionManager.Rollback(status); throw; } transactionManager.Commit(status);
Note that a corresponding 'using TransactionManagerScope' class can be modeled to get similar API usage to System.Transactions TransactionScope.
Programmatic transaction management is usually a good idea only if you have a small number of transactional operations. For example, if you have a web application that require transactions only for certain update operations, you may not want to set up transactional proxies using Spring or any other technology. In this case, using the TransactionTemplate may be a good approach. On the other hand, if your application has numerous transactional operations, declarative transaction management is usually worthwhile. It keeps transaction management out of business logic, and is not difficult to configure in Spring.
You can query the status of the current Spring managed transaction with the class TransactionSynchronizationManager
. Typical application code should not need to rely on using this class but in some cases it is convenient to receive events around the lifecycle of the transaction, i.e. before committing, after committing. TransactionSynchronizationManager
provides a method to register a callback object that is informed on all significant stages in the transaction lifecycle. Note that you can register for lifecycle call back information for any of the transaction managers you use, be it NHibernate or local ADO.NET transactions.
The method to register a callback with the TransactionSynchronizationManager
is
public static void RegisterSynchronization( ITransactionSynchronization synchronization )
Please refer to the SDK docs for information on other methods in this class.
The ITransactionSynchronization
interface is
public interface ITransactionSynchronization { // Typically used by Spring resource management code void Suspend(); void Resume(); // Transaction lifeycyle callback methods // Typically used by Spring resource management code but maybe useful in certain cases to application code void BeforeCommit( bool readOnly ); void AfterCommit(); void BeforeCompletion(); void AfterCompletion( TransactionSynchronizationStatus status ); }
The TransactionSynchronizationStatus
is an enum with the values Committed, Rolledback, and Unknown.