NHibernate:如何在调用session.Save()前为实体分配ID?
如何在调用session.Save()前为NHibernate实体分配HiLo生成的ID?
我需要在将实体保存到数据库前为其分配ID。目前使用HiLo生成器,但ID仅在调用session.Save(object)后才会分配。相关代码示例如下:
var std1 = new Student { FirstName = Guid.NewGuid().ToString(), LastName = Guid.NewGuid().ToString() }; //--- 我需要在这里(对象构造完成后)就分配好唯一ID Console.WriteLine($">>> StudentId before {std1.ID} Name {std1.FirstName}"); Console.WriteLine("<press enter>"); Console.ReadLine(); session.Save(std1); //--- 现在只有调用Save后才会分配ID,这对我的需求来说太晚了 Console.WriteLine($">>> StudentId after {std1.ID}");
Student实体的映射文件定义如下:
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="CSharpTests" namespace="CSharpTests"> <class name="Student" table="student"> <id name="ID" column="id"> <generator class="hilo"> <param name="table">students_hilo</param> <param name="column">next_hi</param> <param name="max_lo">100</param> </generator> </id> <property name="LastName" column="last_name" type="String"/> <property name="FirstName" column="first_name" type="String"/> </class> </hibernate-mapping>
解决方案
当然可以在调用session.Save()前分配HiLo生成的ID,有两种常用方式:
1. 使用session.Lock()触发ID生成
调用session.Lock()方法并传入LockMode.None,会让NHibernate为实体分配ID,但不会将实体持久化到数据库,也不会触发任何数据库锁操作:
var std1 = new Student { FirstName = Guid.NewGuid().ToString(), LastName = Guid.NewGuid().ToString() }; // 提前生成ID session.Lock(std1, LockMode.None); // 此时std1.ID已经被分配 Console.WriteLine($">>> StudentId before {std1.ID} Name {std1.FirstName}"); Console.WriteLine("<press enter>"); Console.ReadLine(); session.Save(std1); Console.WriteLine($">>> StudentId after {std1.ID}");
这种方式的好处是完全由NHibernate管理ID生成逻辑,避免手动操作可能带来的冲突。
2. 手动调用标识符生成器
直接从SessionFactory获取HiLo生成器,手动为实体分配ID:
var std1 = new Student { FirstName = Guid.NewGuid().ToString(), LastName = Guid.NewGuid().ToString() }; // 获取实体对应的标识符生成器 var generator = session.SessionFactory.GetIdentifierGenerator(typeof(Student)); // 生成ID并赋值给实体 std1.ID = (int)generator.Generate(session, std1); // 此时ID已分配完成 Console.WriteLine($">>> StudentId before {std1.ID} Name {std1.FirstName}");
需要注意的是,要根据你的ID类型(比如int、long)调整强制转换的类型。
原理说明
HiLo生成器的工作机制是先从数据库的HiLo表中获取一个hi值,然后在内存中生成lo值(范围0到配置的max_lo),最终ID由hi * (max_lo + 1) + lo计算得出。因此提前生成ID完全可行,因为hi值一旦获取,lo值在内存中分配不会重复,也不会与其他进程/线程生成的ID冲突。
内容的提问来源于stack exchange,提问作者Piotr Aahahhkja
相关产品推荐
相关产品推荐

