首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

NHibernate實戰(7):樂觀並發控制版本號機制詳解

     編輯:關於.NET

前言

多用戶並發修改同一數據時,必須採用並發控制。NHibernate 原生支援樂觀並發,通過 <version> 或 <timestamp> 實現,版本號方式為首選。

並發控制理論基礎

類型核心機制適用場景優缺點
悲觀並發獨占鎖阻塞其他訪問高衝突、鎖成本低於回滾阻塞讀寫、易死鎖、擴展性差
樂觀並發讀取不鎖、更新時檢查版本/時間戳低衝突、回滾成本可接受無阻塞、高併發、需處理衝突重試

NHibernate 採用樂觀並發,符合現代 Web 應用高併發特性。

映射配置詳解

版本號映射 <version>

<version name="Version" column="Version" type="integer" unsaved-value="0"/>
  • type:支援 Int64/Int32/Int16/Ticks/Timestamp/TimeSpan
  • unsaved-value:新實例默認值(0),用於區分瞬時/持久狀態
  • generated:never/always,是否由資料庫生成

時間戳映射 <timestamp>

<timestamp name="LastModified" column="LastModified"/>

等價於 <version type="timestamp">,但版本號更安全、語義更清晰。

實戰驗證流程

1. 實體與映射調整

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 欄位

[Version] [int] NOT NULL DEFAULT 1,或使用 SchemaExport 自動生成。

3. 並發更新測試

[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); // 拋出 StaleObjectStateException
}

SQL 執行流程分析

-- 1. 兩次查詢加載 Version=1
SELECT ... FROM Customer ... WHERE CustomerId=@p0;

-- 2. 第一筆更新成功(Version 1→2)
UPDATE Customer SET Version=2, Firstname='CnBlogs', Lastname='Lee'
WHERE CustomerId=1 AND Version=1;

-- 3. 第二筆更新失敗(WHERE Version=1 不匹配)
UPDATE Customer SET Version=2, Firstname='www.cnblogs.com', Lastname='Lee'
WHERE CustomerId=1 AND Version=1; -- 影響行數 0,拋出異常

核心機制:UPDATE 語句 WHERE 子句包含舊版本號,若已被修改則影響行數為 0,NHibernate 拋出 StaleObjectStateException。

4. 並發刪除測試

[Test]
[ExpectedException(typeof(StaleObjectStateException))]
public void DeleteConcurrencyViolationCanotThrowException()
{
    Customer c1 = _transaction.GetCustomerById(2);
    Customer c2 = _transaction.GetCustomerById(2);
    _transaction.DeleteCustomerTransaction(c1);
    _transaction.DeleteCustomerTransaction(c2); // 同理失敗
}
DELETE FROM Customer WHERE CustomerId=2 AND Version=1; -- 第二次執行影響行數 0

進階場景延伸

  • 跨 Session 並發:Web 應用典型場景,請求間隔長,版本號自動攜帶於實體
  • DTO 傳遞版本:前端提交時包含 Version,後端重新附加到 Session 再更新
  • 衝突處理策略:捕獲異常→重新加載→合併修改→重試,或提示用戶刷新

結語

版本號樂觀並發是 NHibernate 並發控制基石:

  1. 映射添加 <version>,資料表加欄位
  2. NHibernate 自動在 UPDATE/DELETE 中攜帶版本檢查
  3. 衝突時拋出 StaleObjectStateException,業務層決定重試或提示
  4. 優於時間戳:語義明確、避免毫秒級衝突、支援批次更新

下篇將探討事務管理與 Session 生命週期配合。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved