NHibernate之旅(7):初探NHibernate中的并发控制
本節內容
- 什么是并發控制?
- 悲觀并發控制(Pessimistic Concurrency)
- 樂觀并發控制(Optimistic Concurrency)
- NHibernate支持樂觀并發控制
- 實例分析
- 結語
什么是并發控制?
當很多人試圖同一時候改動數據庫中的數據時,必須實現一個控制系統,使一個人所做的改動不會對他人所做的改動產生負面影響。
這稱為并發控制。
簡單的理解就是2個或多個用者同一時候編輯同樣的數據。
這里的用者可能是:實際用戶、不同服務、不同的代碼段(使用多線程),及其在斷開式和連接式情況下可能發生的情況。
并發控制理論依據建立并發控制的方法而分為兩類:
悲觀并發控制(Pessimistic Concurrency)
一個鎖定系統,能夠阻止用戶以影響其它用戶的方式改動數據。假設用戶運行的操作導致應用了某個鎖,僅僅有這個鎖的全部者釋放該鎖。其它用戶才干運行與該鎖沖突的操作。
這樣的方法之所以稱為悲觀并發控制,是由于它主要用于數據爭用激烈的環境中。以及發生并發沖突時用鎖保護數據的成本低于回滾事務的成本的環境中。
簡單的理解通常通過“獨占鎖”的方法。獲取鎖來堵塞對于別的進程正在使用的數據的訪問。換句話說,讀者和寫者之間是會互相堵塞的 ,這可能導致數據同步沖突。
樂觀并發控制(Optimistic Concurrency)
在樂觀并發控制中。用戶讀取數據時不鎖定數據。當一個用戶更新數據時。系統將進行檢查。查看該用戶讀取數據后其它用戶是否又更改了該數據。假設其它用戶更新了數據。將產生一個錯誤。普通情況下,收到錯誤信息的用戶將回滾事務并又一次開始。這樣的方法之所以稱為樂觀并發控制。是因為它主要在下面環境中使用:數據爭用不大且偶爾回滾事務的成本低于讀取數據時鎖定數據的成本。
(以上摘自SQL Server2008 MSDN文檔)
NHibernate支持樂觀并發控制
NHibernate提供了一些方法來支持樂觀并發控制:在映射文件里定義了<version> 節點和<timestamp>節點。當中<version> 節點用于版本號控制,表明表中包括附帶版本號信息的數據。
<timestamp>節點用于時間截跟蹤。表明表中包括時間戳數據。
時間戳本質上是一種對樂觀鎖定不是特別安全的實現。可是通常而言,版本號控制方式是首選的方法。當然。有時候應用程序可能在其它方面使用時間戳。
以下用兩幅圖顯示這兩個節點映射屬性:
看看它們的意義:
- access(默覺得property):NHibernate用于訪問特性值的策略。
- column(默覺得特性名):指定持有版本號號的字段名或者持有時間戳的字段名。
- generated:生成屬性,可選never和always兩個屬性。
- name:持久化類的特性名或者指定類型為.NET類型DateTime的特性名。
- type(默覺得Int32):版本號號的類型,可選類型為Int64、Int32、Int16、Ticks、Timestamp、TimeSpan。注意:<timestamp>和<version type="timestamp">是等價的。
- unsaved-value(在版本號控制中默認是“敏感”值,在時間截默認是null):表示某個實例剛剛被實例化(尚未保存)時的版本號特性值,依靠這個值就能夠把這樣的情況和已經在先前的會話中保存或裝載的游離實例區分開來。(undefined指明使用標識特性值進行推斷)
實例分析
以下用一個樣例來實現樂觀并發控制,這里使用Version版本號控制。
1.改動持久化Customer類:加入Version屬性
public class Customer {public virtual int CustomerId { get; set; }//版本號控制public virtual int Version { get; set; }public virtual string Firstname { get; set; }public virtual string Lastname { get; set; } }2.改動映射文件:加入Version映射節點
<?xml version="1.0" encoding="utf-8" ?> <
hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel"> <class name ="DomainModel.Entities.Customer,DomainModel" table="Customer"> <id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0"> <generator class ="native"></generator> </id> <version name="Version" column="Version" type="integer" unsaved-value="0"/> <property name="Firstname" column ="Firstname" type="string" length="50" not-null="false"/> <property name ="Lastname" column="Lastname" type="string" length="50" not-null="false"/> </class> </hibernate-mapping>3.改動數據庫,加入Version字段
詳細參數:[Version] [int] NOT NULL 默認值為1,當然了改動數據庫是最原始的方式了,假設你會使用SchemaExport,能夠直接利用持久化類和映射文件生成數據庫。以后在介紹怎樣使用這個。
4.并發更新測試
在測試之前,我們先看看數據庫中什么數據,預知一下:
編寫并發更新測試代碼:
查詢2次CustomerId為1的客戶,這里就是上面的第一條數據,第一個改動為"CnBlogs",第二個改動為"www.cnblogs.com",兩者同一時候更新提交。你想想發生什么情況?
[Test] public void UpdateConcurrencyViolationCanotThrowException() {Customer c1 = _transaction.GetCustomerById(1);Customer c2 = _transaction.GetCustomerById(1);c1.Name.Firstname = "CnBlogs";c2.Name.Firstname = "www.cnblogs.com";_transaction.UpdateCustomerTransaction(c1);_transaction.UpdateCustomerTransaction(c2); }讓我們去看看數據庫吧。一目了然:
我們發現CustomerId為1的客戶更新了FirstName數據,而且Version更新為2。
你知道什么原理了嗎?看看這步NHibernate生成的SQL語句(我的可能比你的不一樣):先查詢數據庫,在直接更新數據,看看NHibernate多么實在,明顯做了一些優化工作。
SELECT customer0_.CustomerId as CustomerId3_0_, customer0_.Version as Version3_0_, customer0_.Firstname as Firstname3_0_, customer0_.Lastname as Lastname3_0_, customer0_1_.OrderDiscountRate as OrderDis2_4_0_, customer0_1_.CustomerSince as Customer3_4_0_, case when customer0_1_.CustomerId is not null then 1 when customer0_.CustomerId is not null then 0 end as clazz_0_ FROM Customer customer0_ left outer join PreferredCustomer customer0_1_ on customer0_.CustomerId=customer0_1_.CustomerId WHERE customer0_.CustomerId=@p0; @p0 = '1'UPDATE Customer SET Version = @p0, Firstname = @p1, Lastname = @p2 WHERE CustomerId = @p3 AND Version = @p4; @p0 = '2', @p1 = 'www.cnblogs.com', @p2 = 'Lee', @p3 = '1', @p4 = '1'5.并發刪除測試
我們再來編寫一個測試用于并發刪除。
查詢2次CustomerId為2的客戶。這里就是上面的第二條數據,兩者同一時候刪除數據。
你想想發生什么情況?
[Test] [ExpectedException(typeof(NHibernate.StaleObjectStateException))] public void DeleteConcurrencyViolationCanotThrowException() {Customer c1 = _transaction.GetCustomerById(2);Customer c2 = _transaction.GetCustomerById(2);_transaction.DeleteCustomerTransaction(c1);_transaction.DeleteCustomerTransaction(c2); }同理,看看數據庫里的數據,第二條數據不見了。
其生成SQL的查詢語句同上面一樣,僅僅是一條刪除語句:
DELETE FROM Customer WHERE CustomerId = @p0 AND Version = @p1; @p0 = '2', @p1 = '1'好了,這里通過兩個簡單的實例說明了在NHibernate中對并發控制的支持。
相信有了一定的了解,大家也能夠編寫一些有趣的測試來試試NHibernate中的樂觀并發控制。
結語
這一篇我們初步探索了NHibernate中的并發控制,并用一個典型的實例分析了詳細怎么做。我想這僅僅是蜻蜓點水,很多其它的樂趣就自己探索吧。比方在不同的Session中的并發啊,更新啊,刪除啊......
總結
以上是生活随笔為你收集整理的NHibernate之旅(7):初探NHibernate中的并发控制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MinGW安装和使用基础教程
- 下一篇: 看小说的这些年