2013-04-16 51 views
0

用戶表結構 標識 用戶名(唯一約束)NHibernate的兩種事務讀取相同的表並插入相同的表進行一次交易失敗

我有這樣與NHibernate的問題和SQLServer。 有兩個併發事務試圖在用戶表中插入數據。 這兩個事務都會查詢表中的數據,以檢查要插入的新用戶名是否未出現在表中。 問題是,讓我們說。 Transaction1和Transaction2讀取用戶表,發現用戶表中沒有用戶名embarus。 然後Transaction2嘗試在User1表中插入embarus,而Transaction1已插入並已提交embarus。

因此Transaction2獲得唯一約束的異常。

請幫我解決這個問題,可能有用的任何想法或文章。

我發現SqlServer 2008使用ReadCommitted作爲默認事務隔離級別。

非常感謝。

+1

我不確定你的問題是什麼。 DBMS正在通過標記此問題來幫助您處理代碼。我認爲我們需要更多信息才能提供幫助。 – mickfold

回答

1

您需要捕獲並處理違反唯一約束。最好的方法是創建一個ISqlExceptionConverter實現,將RDBMS特定異常轉換爲應用程序中的自定義異常。

public class SqlServerExceptionConverter : ISQLExceptionConverter 
{ 
    public Exception Convert(AdoExceptionContextInfo adoExceptionContextInfo) 
    { 
     var sqlException = adoExceptionContextInfo.SqlException as SqlException; 
     if (sqlException != null) 
     { 
      // 2601 is unique key, 2627 is unique index; same thing: 
      // http://blog.sqlauthority.com/2007/04/26/sql-server-difference-between-unique-index-vs-unique-constraint/ 
      if (sqlException.Number == 2601 || sqlException.Number == 2627) 
      { 
       return new UniqueKeyException(sqlException.Message, sqlException); 
      } 
     } 
     return adoExceptionContextInfo.SqlException; 
    } 
} 

public class UniqueKeyException : Exception 
{ 
    public UniqueKeyException(string message, Exception innerException) 
     : base(message, innerException) 
    { } 
} 

用法:

  using (var txn = _session.BeginTransaction()) 
      { 
       try 
       { 
        var user= new User 
         { 
          Name = "embarus" 
         }; 
        _session.Save(user); 
        txn.Commit(); 
       } 
       catch (UniqueKeyException) 
       { 
        txn.Rollback(); 
        var msg = string.Format("A user named '{0}' already exists, please enter a different name or cancel.", "embarus"); 
        // Do something useful 
       } 
       catch (Exception ex) 
       { 
        if (txn.IsActive) 
        { 
         txn.Rollback(); 
        } 
        throw; 
       } 
      } 

注意,發生異常後,你不應該重用會話。

+0

非常感謝你的回答。但是,對於此解決方案,我需要逐個插入用戶名。 (可以同時插入多個用戶名) 謝謝 – embarus

+0

我想象你的批量插入會循環遍歷要創建的用戶,並調用一個包含類似於我的答案的代碼的插入方法實際上將它們插入批處理(單個事務)可能會是一個不好的方法,因爲事務會在所有插入失敗的情況下回滾所有插入。 –

+0

非常感謝您,我認爲您的回答是最佳解決方案。 – embarus

相關問題