
NHibernate动态添加表
1. 模板文件
2. 动态映射工具类
3. 动态属性处理器
发布日期:2025-04-20 23:52:52
浏览次数:7
分类:精选文章
本文共 9039 字,大约阅读时间需要 30 分钟。
NHibernate动态添加表:从零到生产环境部署
在实际项目中,动态映射是一项非常有用的功能,可以灵活配置数据库表结构,而无需手动编写hbm.xml文件。本文将详细介绍如何通过NHibernate实现动态表映射,并将其应用于实际项目中。
动态映射的核心概念
动态映射在NHibernate中通过DynamicComponent
标签实现,其核心思想是创建一个可扩展的 ORM 模型,使得在运行时根据实际需要动态添加表字段。这对于处理高度定制化的实体对象非常有用。
自定义实体类的开发
我们首先需要创建一个自定义实体类DynamicTestModel
,它继承自CustomizableEntity
,并添加一个自定义属性auto_id
:
public class DynamicTestModel : CustomizableEntity{ public string EntityName { get; set; } public DynamicTestModel(string entityName) { this.EntityName = entityName; }}
动态映射的实现
1. 模板文件hbm.xml
我们需要一个模板文件hbm_template/Template.hbm.xml
,用于生成动态映射文件。模板文件中包含通用字段和动态组件配置:
2. 动态映射工具类MappingManager
MappingManager
类中添加了两个关键方法UpdateClassMapping
,用于根据实际模型生成动态映射文件:
public static void UpdateClassMapping(DynamicTestModel dynamicModel){ var session = HibernateUtil.Instance.CurrentSession; var fileName = "hbm_template/Template.hbm.xml"; var file = File.ReadAllText(fileName); file = file.Replace("@assembly", dynamicModel.GetType().Assembly.GetName().Name) .Replace("@namespace", dynamicModel.GetType().Namespace) .Replace("@class", dynamicModel.EntityName) .Replace("@table", "Dynamic_" + dynamicModel.EntityName); var xDocument = new XmlDocument(); xDocument.LoadXml(file); var dynamicElements = xDocument.DocumentElement.GetElementsByTagName("dynamic-component"); var dynamicElement = null; if (dynamicElements.Count > 0) { dynamicElements[0].InnerXml = string.Empty; dynamicElement = dynamicElements[0] as XmlElement; } foreach (DictionaryEntry property in dynamicModel.CustomProperties) { var newElement = CreatePropertyElement(xDocument, dynamicElement.NamespaceURI, property); dynamicElement.AppendChild(newElement); } Console.WriteLine(xDocument.OuterXml); xDocument.Save("hbm/" + dynamicModel.EntityName + ".hbm.xml");}
3. 动态属性处理器DynamicModelAccesser
DynamicModelAccesser
类用于处理自定义属性,支持通过反射和属性访问器实现动态字段操作:
public class DynamicModelAccesser : IPropertyAccessor{ private static readonly string CUSTOM_PROPERTY_NAME = "CustomProperties"; private static readonly string AUTO_ID_NAME = "auto_id"; public IGetter GetGetter(System.Type theClass, string propertyName) { return new DynamicGetter(propertyName); } public ISetter GetSetter(System.Type theClass, string propertyName) { return new DynamicSetter(propertyName); } public bool CanAccessThroughReflectionOptimizer { get { return false; } }}
4. 代理工厂与事务 interceptor
为了确保动态映射的正确性,我们需要自定义代理工厂和事务拦截器:
public class DynamicEntityTuplizer : AbstractEntityTuplizer{ private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(DynamicEntityTuplizer)); private static readonly DynamicModelAccesser Accessor = new DynamicModelAccesser(); private sealed class DynamicModelMapInstantiator : IInstantiator { private readonly string entityName; private readonly HashSetisInstanceEntityNames = new HashSet (); private PersistentClass mappingInfo; public DynamicModelMapInstantiator() { this.entityName = null; } public DynamicModelMapInstantiator(PersistentClass mappingInfo) { this.mappingInfo = mappingInfo; this.entityName = mappingInfo.EntityName; isInstanceEntityNames.Add(entityName); if (mappingInfo.HasSubclasses) { foreach (PersistentClass subclassInfo in mappingInfo.SubclassClosureIterator) isInstanceEntityNames.Add(subclassInfo.EntityName); } } public object Instantiate(object id) { return Instantiate(); } public object Instantiate() { return new DynamicTestModel(entityName); } public bool IsInstance(object obj) { var that = obj as DynamicTestModel; if (that != null) { if (entityName == null) { return true; } string type = that.EntityName; return type == null || isInstanceEntityNames.Contains(type); } else { return false; } } } public DynamicEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappingInfo) : base(entityMetamodel, mappingInfo) { Instantiator = BuildInstantiator(mappingInfo); } public override System.Type ConcreteProxyClass { get { return typeof(DynamicTestModel); } } public override bool IsInstrumented { get { return false; } } public override System.Type MappedClass { get { return typeof(DynamicTestModel); } } public override EntityMode EntityMode { get { return EntityMode.Map; } } protected override IGetter BuildPropertyGetter(NHibernate.Mapping.Property mappedProperty, PersistentClass mappedEntity) { return BuildPropertyAccessor(mappedProperty).GetGetter(null, mappedProperty.Name); } protected override ISetter BuildPropertySetter(NHibernate.Mapping.Property mappedProperty, PersistentClass mappedEntity) { return BuildPropertyAccessor(mappedProperty).GetSetter(null, mappedProperty.Name); } protected override IInstantiator BuildInstantiator(PersistentClass mappingInfo) { return new DynamicModelMapInstantiator(mappingInfo); } protected override IProxyFactory BuildProxyFactory(PersistentClass mappingInfo, IGetter idGetter, ISetter idSetter) { IProxyFactory pf = new MapProxyFactory(); try { pf.PostInstantiate(EntityName, null, null, null, null, null); } catch (HibernateException he) { log.Warn("could not create proxy factory for:" + EntityName, he); pf = null; } return pf; }}
测试与验证
在实际项目中,我们需要验证动态映射的正确性。以下是一个简单的测试代码示例:
using DynamicTableTest.DynamicTable;using System;using System.Linq;namespace DynamicTableTest{ class Program { static void Main(string[] args) { DynamicModelMethod(); Console.Read(); } private static void DynamicModelMethod() { var entityName = "Test_Role"; var columnName = "Name"; var columnRole = "Role"; var columnCreateTime = "Create_Time"; var dynamicEntity = new DynamicTestModel(entityName); var name = Guid.NewGuid().ToString(); dynamicEntity.SetValueOfCustomField(columnName, name); dynamicEntity.SetValueOfCustomField(columnRole, 31); dynamicEntity.SetValueOfCustomField(columnCreateTime, DateTime.Now); MappingManager.UpdateClassMapping(dynamicEntity); HibernateUtil.Instance.Reset(); var session = HibernateUtil.Instance.CurrentSession; var trans = session.BeginTransaction(); try { var id = session.Save(entityName, dynamicEntity); Console.WriteLine("first name: " + name); var role = (DynamicTestModel)session.Get(entityName, id); var storedName = role.GetValueOfCustomField(columnName); Console.WriteLine("second name: " + storedName); var newName = Guid.NewGuid().ToString(); role.SetValueOfCustomField(columnName, newName); session.SaveOrUpdate(entityName, role); role = (DynamicTestModel)session.Get(entityName, id); storedName = role.GetValueOfCustomField(columnName); Console.WriteLine("third name: " + storedName); session.Delete(entityName, role); role = (DynamicTestModel)session.Get(entityName, id); Console.WriteLine(role == null); var roles = session.QueryOver(entityName).List(); foreach (var roleItem in roles) { Console.WriteLine(roleItem.ToString()); } Console.WriteLine("###################################"); var query = session.CreateQuery(string.Format("from {0} where {1}='{2}'", entityName, columnName, "abc")); var rolesQuery = query.Enumable().ToList(); foreach (var roleItem in rolesQuery) { Console.WriteLine(roleItem.ToString()); } trans.Commit(); } catch (Exception ex) { trans.Rollback(); Console.WriteLine(ex.ToString()); } } }}
总结
通过以上步骤,我们可以实现NHibernate的动态表映射功能。在实际项目中,可以根据具体需求扩展自定义属性和映射逻辑。动态映射不仅提高了开发效率,还为后续的数据库迁移和扩展提供了更高的灵活性。
发表评论
最新留言
不错!
[***.144.177.141]2025年03月31日 06时53分55秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!