Nhibernate两个事务读取同一个表并插入同一个表会使一个事务失败

本文关键字:事务 同一个 失败 一个 两个 读取 Nhibernate 插入 | 更新日期: 2023-09-27 18:09:29

User表结构Id用户名(唯一约束)

我有Nhibernate和SqlServer这样的问题。有两个并发事务试图在User Table中插入数据。两个事务都查询表中的数据,以检查要插入的新Username是否没有出现在表中。问题是。Transaction1和Transaction2读取用户表,发现用户表中没有用户名embarus。当Transaction1已经在表中插入并提交了embarus时,Transaction2试图在User表中插入embarus。

因此,Transaction2得到唯一约束的异常。

请帮助我解决这个问题,任何可能有用的想法或文章。

我发现SqlServer 2008使用ReadCommitted作为默认事务隔离级别。

Nhibernate两个事务读取同一个表并插入同一个表会使一个事务失败

您需要捕获并处理唯一的约束违反。最好的方法是创建一个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;
                }
            }

请注意,在异常发生后不应该重用会话。