替换SQL Server触发器,用NHibernate实现Code字段自动填充的更优方案
替代触发器的NHibernate Code字段填充优化方案
不用先插再更,直接在插入前处理逻辑就行,给你几个实用的方案:
1. 业务代码层前置处理(最简单直接)
在调用NHibernate的Save/SaveOrUpdate方法前,直接检查实体的Code字段,为空就生成值,一步完成插入:
var yourEntity = new YourEntity(); // 用户已设置Code则跳过,为空自动生成 if (string.IsNullOrWhiteSpace(yourEntity.Code)) { yourEntity.Code = GenerateUniqueCode(); // 这里实现你的Code生成规则,比如GUID截取、自增编号等 } using (var session = sessionFactory.OpenSession()) using (var tx = session.BeginTransaction()) { session.Save(yourEntity); tx.Commit(); }
这种方式逻辑直观,无额外NHibernate配置,适合小项目或单一实体场景。批量插入时循环处理每个实体即可。
2. 用NHibernate拦截器统一处理(全局复用)
如果多个实体需要类似逻辑,或不想在业务代码里重复判断,用IInterceptor集中处理:
实现拦截器
public class CodeAutoFillInterceptor : EmptyInterceptor { public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types) { // 仅处理目标实体类型 if (entity is YourEntity targetEntity && string.IsNullOrWhiteSpace(targetEntity.Code)) { var newCode = GenerateUniqueCode(); targetEntity.Code = newCode; // 更新NHibernate状态数组,确保生成的Code能写入数据库 int codePropIndex = Array.IndexOf(propertyNames, nameof(YourEntity.Code)); if (codePropIndex >= 0) { state[codePropIndex] = newCode; } return true; // 通知NHibernate实体状态已修改 } return base.OnSave(entity, id, state, propertyNames, types); } // 自定义Code生成逻辑,按需调整 private string GenerateUniqueCode() { return Guid.NewGuid().ToString("N").Substring(0, 8); } }
注册拦截器
可全局注册(所有Session生效)或单个Session指定:
// 全局注册 var config = new Configuration(); config.SetInterceptor(new CodeAutoFillInterceptor()); var sessionFactory = config.BuildSessionFactory(); // 单个Session生效 using (var session = sessionFactory.OpenSession(new CodeAutoFillInterceptor())) { // 执行Session操作 }
此方案将Code生成逻辑与业务代码解耦,所有保存操作自动触发检查,适合中大型项目。
3. 用NHibernate事件监听器(生命周期事件处理)
和拦截器类似,事件监听器是NHibernate生命周期事件的扩展点,适合监听多阶段生命周期的场景,比如实现IPreInsertEventListener:
实现事件监听器
public class CodePreInsertListener : IPreInsertEventListener { public bool OnPreInsert(PreInsertEvent @event) { if (@event.Entity is YourEntity targetEntity && string.IsNullOrWhiteSpace(targetEntity.Code)) { var newCode = GenerateUniqueCode(); targetEntity.Code = newCode; // 更新插入状态数组,确保值写入数据库 int codePropIndex = Array.IndexOf(@event.Persister.PropertyNames, nameof(YourEntity.Code)); if (codePropIndex >= 0) { @event.State[codePropIndex] = newCode; } } return false; // 返回false继续执行默认插入流程 } private string GenerateUniqueCode() { return $"CODE_{DateTime.Now:yyyyMMddHHmmss}_{Guid.NewGuid().ToString("N").Substring(0,4)}"; } }
注册监听器
var config = new Configuration(); var listenerRegistry = config.EventListeners; // 添加自定义监听器到前置插入事件列表 listenerRegistry.PreInsertEventListeners = new IPreInsertEventListener[] { new CodePreInsertListener() } .Concat(listenerRegistry.PreInsertEventListeners ?? Array.Empty<IPreInsertEventListener>()) .ToArray(); var sessionFactory = config.BuildSessionFactory();
注意事项
- Code唯一性保障:若需全局唯一,生成逻辑要考虑并发场景,比如调用数据库序列(NHibernate的
SequenceGenerator)或用分布式ID生成器,避免重复。 - 批量插入支持:三个方案均支持批量插入,拦截器和事件监听器会自动处理每个实体。
- 性能优势:均为单次INSERT操作,比「插入+更新」减少一次数据库交互,降低事务内锁开销。
内容的提问来源于stack exchange,提问作者eray
相关产品推荐
相关产品推荐

