NHibernate动态扩展属性小记
http://www.infoq.com/articles/hibernate-custom-fields 的NHibernate实现因为动态扩展表需要修改hbm.xml文件,所以hbm.xml文件必须放在可编辑的路径中。
- hibernate.cfg.xml中添加节点
1 <property name="hbm2ddl.auto">update</property>
- 假设我有一张表Contract,hbm文件如下
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping 3 xmlns="urn:nhibernate-mapping-2.2" 4 assembly="Test" 5 namespace="Test.DynamicEntityTest" 6 auto-import="true" 7 default-access="property" 8 default-cascade="none" 9 default-lazy="true"> 10 11 <class name="Contract" table="contract"> 12 <id name="Id" column="id"> 13 <generator class="native" /> 14 </id> 15 16 <property name="Name" column="name" type="string" /> 17 18 <dynamic-component insert="true" name="CustomProperties" optimistic-lock="true" unique="false" update="true"> 19 </dynamic-component> 20 21 </class> 22 </hibernate-mapping>
- 对应类为
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace Test.DynamicEntityTest 7 { 8 public class Contract : CustomizableEntity 9 { 10 public virtual int Id { get; set; } 11 public virtual string Name { get; set; } 12 } 13 }
- Contract类继承自CustomizeableEntity
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace Test.DynamicEntityTest 7 { 8 public abstract class CustomizableEntity 9 { 10 private IDictionary<string, object> customProperties; 11 12 public virtual IDictionary<string, object> CustomProperties 13 { 14 get 15 { 16 if (customProperties == null) 17 { 18 customProperties = new Dictionary<string, object>(); 19 } 20 return customProperties; 21 } 22 set 23 { 24 this.customProperties = value; 25 } 26 } 27 28 public virtual object GetValueOfCustomField(string name) 29 { 30 return CustomProperties[name]; 31 } 32 33 public virtual void SetValueOfCustomField(string name, object value) 34 { 35 CustomProperties[name] = value; 36 } 37 } 38 }
- 我们再创建一个HibernateUtil,添加了从我们指定的目录读取hbm.xml文件
1 using NHibernate; 2 using NHibernate.Cfg; 3 using NHibernate.Cfg.MappingSchema; 4 using NHibernate.Mapping; 5 using System; 6 using System.Collections.Generic; 7 using System.IO; 8 using System.Linq; 9 using System.Text; 10 using System.Xml; 11 12 namespace Test.DynamicEntityTest 13 { 14 public class HibernateUtil 15 { 16 private static HibernateUtil instance; 17 private Configuration configuration; 18 private ISessionFactory sessionFactory; 19 private ISession session; 20 21 public static HibernateUtil Instance 22 { 23 get 24 { 25 if (instance == null) 26 { 27 instance = new HibernateUtil(); 28 } 29 return instance; 30 } 31 } 32 33 private ISessionFactory SessionFactory 34 { 35 get 36 { 37 if (sessionFactory == null) 38 { 39 var config = Configuration.Configure(); 40 41 var dir = new DirectoryInfo("hbm"); 42 foreach (var file in dir.GetFiles("*.hbm.xml")) 43 { 44 var typeName = file.Name.Substring(0, file.Name.IndexOf('.')); 45 var namedDocument = config.LoadMappingDocument(new XmlTextReader(file.FullName), null); 46 config.AddDeserializedMapping(namedDocument.Document, namedDocument.Name); 47 } 48 49 sessionFactory = config.BuildSessionFactory(); 50 } 51 return sessionFactory; 52 } 53 } 54 55 private Configuration Configuration 56 { 57 get 58 { 59 if (configuration == null) 60 { 61 configuration = new Configuration(); 62 } 63 return configuration; 64 } 65 } 66 67 public ISession CurrentSession 68 { 69 get 70 { 71 if (session == null) 72 { 73 session = SessionFactory.OpenSession(); 74 } 75 return session; 76 } 77 } 78 79 public void Reset() 80 { 81 if (session != null) 82 { 83 session.Flush(); 84 if (session.IsOpen) 85 { 86 session.Close(); 87 } 88 } 89 90 if (sessionFactory != null) 91 { 92 sessionFactory.Close(); 93 } 94 95 this.configuration = null; 96 this.sessionFactory = null; 97 this.session = null; 98 } 99 100 public PersistentClass getClassMapping(Type entityClass) 101 { 102 return Configuration.GetClassMapping(entityClass); 103 } 104 } 105 }
- 还有CustomEntityManager用于添加自定义属性
1 using NHibernate.Cfg; 2 using NHibernate.Mapping; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 8 namespace Test.DynamicEntityTest 9 { 10 public class CustomizableEntityManager 11 { 12 private Component customProperties; 13 public Type EntityType { get; private set; } 14 15 public CustomizableEntityManager(Type entityType) 16 { 17 this.EntityType = entityType; 18 } 19 20 public Component CustomProperties 21 { 22 get 23 { 24 if (customProperties == null) 25 { 26 var property = PersistentClass.GetProperty("CustomProperties"); 27 customProperties = (Component)property.Value; 28 } 29 30 return customProperties; 31 } 32 } 33 34 public void AddCustomFields(string[] names) 35 { 36 foreach (var name in names) 37 { 38 InnerAddCustomField(name); 39 } 40 UpdateMapping(); 41 } 42 43 public void AddCustomField(string name) 44 { 45 InnerAddCustomField(name); 46 UpdateMapping(); 47 } 48 49 private void InnerAddCustomField(string name) 50 { 51 var simpleValue = new SimpleValue(); 52 simpleValue.AddColumn(new Column(name)); 53 simpleValue.TypeName = typeof(string).Name; 54 55 var persistentClass = PersistentClass; 56 simpleValue.Table = persistentClass.Table; 57 58 var property = new Property(); 59 property.Name = name; 60 property.Value = simpleValue; 61 CustomProperties.AddProperty(property); 62 } 63 64 private void UpdateMapping() 65 { 66 MappingManager.UpdateClassMapping(this); 67 HibernateUtil.Instance.Reset(); 68 } 69 70 private PersistentClass PersistentClass 71 { 72 get 73 { 74 return HibernateUtil.Instance.getClassMapping(this.EntityType); 75 } 76 } 77 } 78 }
- 最后是MappingManager,用来修改hbm.xml文件
1 using NHibernate.Mapping; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Reflection; 6 using System.Text; 7 using System.Xml; 8 using System.Xml.Linq; 9 10 namespace Test.DynamicEntityTest 11 { 12 public class MappingManager 13 { 14 private static const string DYNAMIC_COMPONENT_TAG = "dynamic-component"; 15 16 public static void UpdateClassMapping(CustomizableEntityManager entityManager) 17 { 18 var session = HibernateUtil.Instance.CurrentSession; 19 var entityType = entityManager.EntityType; 20 var fileName = string.Format("hbm/{0}.hbm.xml", entityType.Name); 21 var xDocument = new XmlDocument(); 22 xDocument.Load(fileName); 23 24 var dynamicElements = xDocument.DocumentElement.GetElementsByTagName(DYNAMIC_COMPONENT_TAG); 25 XmlElement dynamicElement = null; 26 if (dynamicElements.Count > 0) 27 { 28 dynamicElements[0].InnerXml = string.Empty; 29 dynamicElement = dynamicElements[0] as XmlElement; 30 } 31 32 foreach (var property in entityManager.CustomProperties.PropertyIterator) 33 { 34 var newElement = CreatePropertyElement(xDocument,dynamicElement.NamespaceURI, property); 35 dynamicElement.AppendChild(newElement); 36 } 37 38 Console.WriteLine(xDocument.OuterXml); 39 xDocument.Save(fileName); 40 } 41 42 private static XmlElement CreatePropertyElement(XmlDocument document,string parentNamespace, NHibernate.Mapping.Property property) 43 { 44 var element = document.CreateElement("property", parentNamespace); 45 46 element.SetAttribute("name", property.Name); 47 element.SetAttribute("column", ((Column)property.ColumnIterator.First()).Name); 48 element.SetAttribute("type", property.Type.ReturnedClass.Name); 49 element.SetAttribute("not-null", "false"); 50 return element; 51 } 52 } 53 }
- 现在是测试代码
1 using System; 2 using Test.DynamicEntityTest; 3 4 namespace Test 5 { 6 public class Program 7 { 8 static void Main(params string[] args) 9 { 10 CustomEntityTest(); 11 Console.Read(); 12 } 13 14 static void CustomEntityTest() 15 { 16 var fieldName = "email"; 17 var value = "alex@test.com"; 18 var session = HibernateUtil.Instance.CurrentSession; 19 var contactEntityManager = new CustomizableEntityManager(typeof(Contract)); 20 contactEntityManager.AddCustomField(fieldName); 21 22 session = HibernateUtil.Instance.CurrentSession; 23 var trans = session.BeginTransaction(); 24 try 25 { 26 //add 27 var contact = new Contract(); 28 contact.Name = "Contact Name"; 29 contact.SetValueOfCustomField(fieldName, value); 30 var id = session.Save(contact); 31 32 //get 33 contact = session.Get<Contract>(id); 34 var contactValue = contact.GetValueOfCustomField(fieldName); 35 Console.WriteLine("Value: " + contactValue); 36 37 //update 38 contact.SetValueOfCustomField(fieldName, "test@test.com"); 39 session.SaveOrUpdate(contact); 40 41 //delete 42 session.Delete(contact); 43 44 trans.Commit(); 45 } 46 catch (Exception ex) 47 { 48 trans.Rollback(); 49 Console.WriteLine(ex.ToString()); 50 } 51 } 52 } 53 }
Over!