Spring.NET學習筆記16——事務管理(應用篇)

 目前有很多種數據訪問技術。在.NET FCL中,有三類API可以執行事務管理,分別是ADO.NET、企業服務和System.Transactions。其它的數據訪問技術,如對象關係映射(object relational mappers)和結果集映射(result-set mapping)等等的應用也很廣泛,每種技術也都有自己的事務管理API。事務管理的代碼一般是直接和各種事務API綁定在一起的,所以在開發時必須根據所用的具體技術來決定採用哪種API。但是,這種代碼與事務API的緊耦合決定了很難通過簡單的重構來解決更換數據訪問技術的問題。而Spring.NET的事務框架允許在各種數據訪問技術之上使用相同的API。通過配置或者集中的編程方式,可以很容易的更換後臺事務API,而不需要對代碼進行“大修”。

  我們可以用業界公認的最佳方式來建立一種數據訪問機制。Martin Fowler的著作《Patterns of Enterprise Application Architecture》講到了許多在實際應用中非常成功的數據訪問方法。其一便是在應用程序架構中引入一個數據訪問層。數據訪問層不僅要考慮到與不同的數據庫和數據訪問技術的兼容性,而且職責要嚴格限制在數據訪問功能上。數據訪問層應該只包含數據訪問對象(DAO)以及“創建/獲取/更新/刪除”(CRUD,Create/Retrieve/Update/Delete)的操作,不應該涉及任何業務邏輯。業務邏輯應該位於單獨的業務服務層,並且需要與一或多個DAO協作來完成高層次的用戶功能。

爲了在事務中“要麼全執行要麼全不執行”這些用戶功能,事務環境(transaction context)就應該由業務服務層(或某個“更高”的層次)控制。在實現上,一個很重要的細節是如何讓DAO瞭解在其它層次中開始的“外部”事務。如果讓DAO自己負責連接和事務的管理,就把問題看的過於簡單化了,因爲此時每個DAO都會執行自己的事務/資源管理,所以無法在同一事務中執行多個DAO操作。我們需要一種有效的手段,將連接/事務成對的從業務服務層傳遞給DAO。方法有很多種,最不具侵入性的就是將連接/事務作爲方法參數顯式的傳遞給DAO。另一種方法是將連接/事務放在線程本地存儲內。不管使用哪種方法,只要在用ADO.NET,就必須得自己創建一個基礎框架來完成這個任務。

  但是,等一下,企業服務不是能解決這個問題嗎——還有System.Transactions命名空間呢?關於這個嘛,答案是對...也不對。企業服務確實能夠讓我們在事務環境中使用“原生”的ADO.NET在同一事務中執行多個DAO操作。但它的缺點是必須通過MS-DTC(Microsoft Distributed Transaction Coordinator)使用分佈式(全局的)事務。如果只爲了使用全局事務就必須依賴MS-DTC,那應用程序在性能上就會大打折扣了。

使用.NET2.0新增System.Transactions命名空間下的TransactonScope類時,也有相同的問題。TransactonScope類的目的實際上是——用using語句使一段代碼成爲事務性代碼。只要訪問事務性的資源,using語句中的普通ADO.NET代碼就會在一個ADO.NET本地事務中運行。但是,System.Transactions(和數據庫)的“神奇“之處在於,如果需要訪問第二個事務性資源,本地事務就會升級爲分佈式事務。這個過程叫做PSPE(Promotable Single Phase Enlistment)。此外,還需提醒讀者:在同一個數據庫上使用同一連接字符串打開第二個連接,就會使本地事務升級爲分佈式事務。所以,如果每個DAO都執行自己的連接管理,那就完了:本地事務會突然升級爲分佈式事務!如果應用程序只使用一個數據庫,要想避免這個問題,就必須將唯一的連接對象傳遞給系統中的所有DAO。另外要注意的是某些數據庫不支持PSPE,就算用單個數據庫連接也會使用分佈式事務(比如Oracle)。

Spring.NET的聲明式事務管理功能非常強大。在數據庫事務領域,討論聲明式事務管理的話題並不是很多,因爲現在開發人員已經可以不再直接用凌亂繁複的事務API來進行事務管理了,而是可以通過在類和方法上應用某些特性來進行。但是在FCL中,只有企業服務提供了這一功能。Spring.NET則填補了這個空白——不管使用哪種事務管理技術:ADO.NET,還是(最常用的)System.Transactions,都可以使用聲明式的事務管理。另外,企業服務也有自己的問題,舉例來說,如果需要爲查詢/讀取操作和創建/更新/刪除操作設置不同的隔離等級,就必須將這兩組操作分隔在不同的類中實現,因爲在企業服務中,聲明式事務的元數據只對類有效。但總的來說,企業服務,特別是在XP sp2和Server 2003中新出現的“無組件服務”,還有在應用程序進程內駐留的功能都是相當不錯的。不過,雖然有這些優點,企業服務仍尚未在開發社區中引起很大的關注。

Spring.NET事務管理的宗旨,就是要減輕FCL或第三方數據訪問技術給開發人員帶來的這些“痛苦”。它支持聲明式事務管理,可以用配置方式獲取事務選項的元數據——目前支持兩種聲明方式:在代碼中用.NET特性聲明;在IoC容器中用XML聲明。

最後,Spring.NET事務管理還允許在同一事務中使用不同的數據訪問技術——例如混合使用ADO.NET和NHibernate

好了,再講估計就有點煩人了,現在我們來看代碼。

  在Spring.NET中,提供了以下實現類:

    AdoPlatformTransactionManager- 基於本地ADO.NET的事務。

    ServiceDomainPlatformTransactionManager- 由企業服務提供的分佈式事務管理器。

    TxScopePlatformTransactionManager- 由System.Transactions提供的本地/分佈式的事務管理器。

  

  ITransactionDefinition接口封裝了以下信息:

    Isolation:該事務對其它事務操作的隔離級別。例如,用來表示某個事務是否能看到其它事務寫入的、但尚未提交的信息。

    Propagation:一般情況下,TransactionScope內的代碼都會在爲其指定的事務中運行。但是,該屬性可用來設置如果某個事務環境已經存在時,該事務內的方法是否要執行:比如說,是簡單的讓它在現有事務中繼續運行呢(這是一般情況),還是掛起現有事務然後創建一個新事務來運行。

    Timeout:在超時(並且被事務基礎框架自動回滾之前)前該事務可以運行多久。

    Read-only狀態:只讀的事務不會修改任何數據。在某些情況下(比如使用NHibernate時),只讀事務能顯著提高性能。

 

   讓我們從實現代碼中學習Spring.NET事務管理的機制。
   準備條件:數據中建了2張表,如圖1

UserTable爲存儲用戶信息的表,AccountTable爲存儲用戶賬號的信息。

數據庫訪問層:

 

 

 

業務處理層:當我們插入用戶信息後,需要爲這個用戶建立一個賬號,當我們刪除用戶時,需要將帳號信息一起刪除。這裏我們就會用到了事務。

 

 

 配置:

 

 

 

我們新建一個單元測試:

 

 

輸出效果:兩條數據已經插入數據庫(圖2)

圖2

 

我們修改一下UserService的DeleteData方法,在調用 UserDao.Delete(name)以後拋出異常,測試數據是否回滾。

 

如果數據沒有回滾,則我們得到的數據爲UserTable表中沒有數據,AccountTable表中有數據。

 

 

輸出效果:圖3
圖3

 

  我們發現數據已經回滾。

  Spring.NET框架幫我們很好的管理了事務。但在我們的編碼過程中經常使用到try...catch語句。要是在DeleteData方法中加入try...catch語句會回滾嗎?我們修改一下UserService的SaveData方法

 

當前的數據庫AccountTable表中已經存在字段爲AccountName的“123456”記錄。因爲AccountName字段是主鍵,我再插入一條數據就會出現異常。

 

輸出效果:圖4
圖4

 

  我們發現在使用try...catch語句以後並沒有回滾。從以上的效果中,我們可以得出結論:使用try...catch的異常叫作“運行期異常”,Spring.NET的事務管理默認是不對運行期異常回滾的。

  如果要實現沒有try...catch語句不回滾事務,我們需要在方法上標記[Transaction(NoRollbackFor = new Type[] { typeof(Exception) })]。NoRollbackFor 屬性是Type數組,意思是運到Exception異常就不回滾,您也可以在增加其它的異常條件,如typeof(ArithmeticException)。這樣,當遇到標有包含NoRollbackFor 屬性的異常時,就不進行回滾。

  代碼下載

 

  參考Spring.NET中文手冊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章