對於數據訪問當然會涉及到事務管理,對於 NHibernate 來說,通常我們使用下面的方式進行。
// 創建新公司 var session = Assyria.DataAccess.SessionFactory.GetCurrentSession(); using (NHibernate.ITransaction transaction = session.BeginTransaction()) { session.Save(company); transaction.Commit(); }
會話的 BeginTransaction 用來啟動事務管理,Commit 方法用來顯式提交事務。
在 Spring.NET 中,對於事務管理提供了完善的支持,尤其與 NHibernate 的集成,更是非常方便。
首先,Spring.NET 在程序集 Spring.Data.NHibernate32 中提供了 Spring.Data.NHibernate.LocalSessionFactoryObject,作為會話工廠對象。然后,通過在 Spring.Data.NHibernate32 中提供的 Spring.Data.NHibernate.HibernateTransactionManager 實現 Nhibernate 的事務管理。
具體事務的支持,可以通過特性來實現。
<tx:attribute-driven transaction-manager="transactionManager"/>
這樣,數據訪問的配置文件一般如下所示。
<?xml version="1.0" ?> <objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database" xmlns:tx="http://www.springframework.net/tx" > <!--描述--> <description> 數據訪問的配置信息 包括:DbProvider NHibernate 異常處理 事務處理 </description> <!-- 通過主應用程序的上下文配置文件引用 --> <object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"> <property name="ConfigSections" value="databaseSettings"/> </object> <!-- 數據庫的配置 --> <db:provider id="DbProvider" provider="SqlServer-2.0" connectionString="Data Source=${db.server};Database=TSQLFundamentals2008;Integrated Security=true;" /> <!-- NHibernate 配置 --> <!-- 可以通過 name 為其指定別名 name="SessionFactory" --> <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject,Spring.Data.NHibernate32" > <!-- 關於數據庫連接的配置,直接使用 DbProvider 中的設置,這樣,不需要為 Hibernate 再提供連接串和驅動 --> <property name="DbProvider" ref="DbProvider"/> <!-- 包含有映射文件的程序集,需要分析的hbm程序集名稱 --> <property name="MappingAssemblies"> <list> <value>Forbetter.Domain</value> </list> </property> <!-- 其他的參數 --> <property name="HibernateProperties"> <dictionary> <!-- 方言 --> <entry key="dialect" value="NHibernate.Dialect.MsSql2008Dialect"/> <entry key="use_proxy_validator" value="false" /> <entry key="show_sql" value="true"/> </dictionary> </property> <!-- 與 Spring 的聲明式事務集成 --> <property name="ExposeTransactionAwareSessionFactory" value="true" /> </object> <!-- 事務管理策略,本地數據庫事務 --> <!----> <object id="transactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32"> <property name="DbProvider" ref="DbProvider"/> <property name="SessionFactory" ref="NHibernateSessionFactory"/> </object> <!--支持事務的特性--> <!----> <tx:attribute-driven transaction-manager="transactionManager"/> <!-- 持久化異常轉換處理 --> <object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/> </objects>
在代碼中,通過事務的特性來聲明需要的事務即可。這個特性定義在 Spring.Transaction.Interceptor 命名空間下,名為 Transaction。
可以通過 Transaction 聲明事務的傳播以及隔離性。
事務的傳播行為有如下 7 種,定義在 Spring.Transaction.TransactionPropagation 枚舉中,可以通過事務特性的 TransactionPropagation 屬性進行聲明。默認為 TransactionPropagation.Required。
- REQUIRED:支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
- SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行。
- MANDATORY:支持當前事務,如果當前沒有事務,就拋出異常。
- REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
- NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
- NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。
隔離性有如下 7 種,可以通過事務標簽的 IsolationLevel 進行聲明。默認為 IsolationLevel.ReadCommitted。
- Chaos:無法覆蓋隔離級別更高的事務中的掛起的更改。
- ReadCommitted:在正在讀取數據時保持共享鎖,以避免臟讀,但是在事務結束之前可以更改數據,從而導致不可重復的讀取或幻像數據。
- ReadUncommitted:可以進行臟讀,意思是說,不發布共享鎖,也不接受獨占鎖。
- RepeatableRead:在查詢中使用的所有數據上放置鎖,以防止其他用戶更新這些數據。防止不可重復的讀取,但是仍可以有幻像行。
- Serializable:在 System.Data.DataSet 上放置范圍鎖,以防止在事務完成之前由其他用戶更新行或向數據集中插入行。
- Snapshot:通過在一個應用程序正在修改數據時存儲另一個應用程序可以讀取的相同數據版本來減少阻止。表示您無法從一個事務中看到在其他事務中進行的更改,即便重新查詢也是如此。
- Unspecified:正在使用與指定隔離級別不同的隔離級別,但是無法確定該級別。
ReadOnly屬性用來設置事務是否是只讀的,默認是假。為 read/write.
NoRollbackFor 和 RollbackFor 用來設置事務的回滾策略,分別用來設置對於哪些異常不需要回滾和哪些異常需要回滾。默認為任何異常都導致回滾。
使用方式如下。
public class FulfillmentService : IFulfillmentService { // fields and properties for dao object omitted, see above [Transaction(ReadOnly = false)] public void ProcessCustomer(string customerId) { //Find all orders for customer Customer customer = CustomerDao.FindById(customerId); foreach (Order order in customer.Orders) { //Validate Order Validate(order); //Ship with external shipping service ShippingService.ShipOrder(order); //Update shipping date order.ShippedDate = DateTime.Now; //Update shipment date OrderDao.SaveOrUpdate(order); //Other operations...Decrease product quantity... etc } } }
如果你不喜歡使用特性來標注事務,Spring.NET為NHibernate提供的事務代理是 TransactionProxyFactoryObject。也可以使用下面的配置方式來取代特性。
<object id="TxProxyConfigurationTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="HibernateTransactionManager"/> <property name="TransactionAttributes"> <name-values> <!-- Add common methods across your services here --> <add key="Process*" value="PROPAGATION_REQUIRED"/> </name-values> </property> </object>
注意寫法有些變化,對於事務的傳播增加了前綴 PROPAGATION_