2013-04-16 19 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

相关问题