[轉]NHibernate之旅(7):初探NHibernate中的並發控制

來源:互聯網
上載者:User

標籤:blog   http   io   使用   ar   strong   檔案   資料   sp   

本節內容

  • 什麼是並發控制?
    • 封閉式並行存取控制(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 [email protected]; @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之旅(7):初探NHibernate中的並發控制

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.