【问题标题】:How to increment ID before any insert with NHibernate如何在使用 NHibernate 进行任何插入之前增加 ID
【发布时间】:2011-10-25 10:29:18
【问题描述】:

看起来 NH 只获取一次 MAX(ID),首先插入然后在内部存储这个值,这在其他进程插入数据时给我带来了一些问题。然后我没有实际的 ID 并引发重复键异常。

假设我们有表Cats

CREATE TABLE Cats(ID int, Name varchar(25))

然后我们用 FluentNhibernate 做了相应的映射

public class CatMap : ClassMap<Cat>
{
    public CatMap()
    {
      Id(m=>m.ID).GeneratedBy.Increment();
      Map(m=>.Name);
    }
}

我想要实现的只是插入我的Cat 记录,其ID 由NHibernate 生成,使用SELECT MAX(ID) FROM Cats 在任何插入之前。在任何提交 dosnt 工作后执行 Session.Flush。我已经使用 SQL Server 探查器进行了一些调查,并且这个 sql 语句只执行一次(在第一次插入时) - 其他插入不会强制检索实际的 MAX(ID)。我知道像 HiLo 这样的其他算法更好,但我无法替代它。

【问题讨论】:

    标签: nhibernate auto-increment


    【解决方案1】:

    如您所见,NHibernate Increment id 生成器不适用于多用户环境。您说使用 HiLo 生成器不是一种选择,因此您有以下选择:

    • 使用 Native 生成​​器并更改 id 列以使用数据库支持的身份机制

    • 使用 Assigned 生成器并编写代码来确定下一个有效 id

    • 创建一个自定义生成器,您可以在其中实现 IIdentifierGenerator 接口来满足您的需要

    以下是自定义生成器的示例代码,该生成器使用通用过程来获取给定表的 ID。这种方法的主要问题是您必须将代码包装在 Unit of Work 模式中,以确保“select max(id) ...”和插入被相同的数据库事务覆盖。IIdentifierGenerator 链接具有连接此自定义生成器所需的 XML 映射。

    using System;
    using System.Collections.Generic;
    using System.Data;
    using NHibernate.Dialect;
    using NHibernate.Engine;
    using NHibernate.Id;
    using NHibernate.Persister.Entity;
    using NHibernate.Type;
    
    namespace YourCompany.Stuff
    {
        public class IdGenerator : IIdentifierGenerator, IConfigurable
        {
            private string _tableName;
            // The "select max(id) ..." query will go into this proc:
            private const string DefaultProcedureName = "dbo.getId";
    
            public string ProcedureName { get; protected set; }
            public string TableNameParameter { get; protected set; }
            public string OutputParameter { get; protected set; }
    
            public IdGenerator()
            {
                ProcedureName = DefaultProcedureName;
                TableNameParameter = "@tableName";
                OutputParameter = "@newID";
            }
    
            public object Generate(ISessionImplementor session, object obj)
            {
                int newId;
                using (var command = session.Connection.CreateCommand())
                {
                    var tableName = GetTableName(session, obj.GetType());
    
                    command.CommandType = CommandType.StoredProcedure;
                    command.CommandText = ProcedureName;
    
                    // Set input parameters
                    var parm = command.CreateParameter();
                    parm.Value = tableName;
                    parm.ParameterName = TableNameParameter;
                    parm.DbType = DbType.String;
    
                    command.Parameters.Add(parm);
    
                    // Set output parameter
                    var outputParameter = command.CreateParameter();
                    outputParameter.Direction = ParameterDirection.Output;
                    outputParameter.ParameterName = OutputParameter;
                    outputParameter.DbType = DbType.Int32;
    
                    command.Parameters.Add(outputParameter);
    
                    // Execute the stored procedure
                    command.ExecuteNonQuery();
    
                    var id = (IDbDataParameter)command.Parameters[OutputParameter];
    
                    newId = int.Parse(id.Value.ToString());
    
                    if (newId < 1)
                        throw new InvalidOperationException(
                            string.Format("Could not retrieve a new ID with proc {0} for table {1}",
                                          ProcedureName,
                                          tableName));
                }
    
                return newId;
            }
    
            public void Configure(IType type, IDictionary<string, string> parms, Dialect dialect)
            {
                _tableName = parms["TableName"];
            }
    
            private string GetTableName(ISessionImplementor session, Type objectType)
            {
                if (string.IsNullOrEmpty(_tableName))
                {
                    //Not set by configuration, default to the mapped table of the actual type from runtime object:
                    var persister = (IJoinable)session.Factory.GetClassMetadata(objectType);
    
                    var qualifiedTableName = persister.TableName.Split('.');
                    _tableName = qualifiedTableName[qualifiedTableName.GetUpperBound(0)]; //Get last string
                }
    
                return _tableName;
            }
        }
    }
    

    【讨论】:

    • 通过实现自定义 ID 生成器来完成。
    • @Dariusz 我遇到了类似的问题,您能否提供一些有关您如何实现此功能的其他信息?
    • 正如我所说,我已经实现了我的自定义 IdGenerator @nozzleman
    猜你喜欢
    • 1970-01-01
    • 2020-09-23
    • 2017-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多