NHibernate动态添加表
发布日期: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 HashSet
isInstanceEntityNames = 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的动态表映射功能。在实际项目中,可以根据具体需求扩展自定义属性和映射逻辑。动态映射不仅提高了开发效率,还为后续的数据库迁移和扩展提供了更高的灵活性。

上一篇:NHibernate学习[1]
下一篇:NHibernate使用之详细图解

发表评论

最新留言

不错!
[***.144.177.141]2025年03月31日 06时53分55秒

关于作者

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

推荐文章