对ORM(Object/Relation Mapping)技术了解的都知道,NHibernate是基于.Net的一种数据持久化的框架。之前在Java里了解过Hibernate框架,但平时开发还是.Net用得比较多,而且目前面向对象和关系型数据库的开发应用仍然是主流,所以觉得有必要去了解学习NHibernate这个优秀的ORM框架。
一、类库
在我的这个NHibernate2.1.2.GA版的程序事例中,是使用VS2005工具开发的,引用NHibernate的相关类库文件如下:
名称 | 功能 |
---|---|
NHibernate.dll | NHibernate核心类库 |
Iesi.Collections.dll | 提供集合运算功能 |
NHibernate.ByteCode.Castle.dll | 使用Castle动态代理 |
Castle.Core.dll | Castle代理核心类库 |
Castle.DynamicProxy2.dll | Castle动态代理 |
Antlr3.Runtime.dll | 语法分析和词法分析(未使用) |
二、配置
NHibernate可以有几种加载配置的方式:Web.Config/App.Config、hibernate.cfg.xml、编码指定配置文件。因为在我的这个事例是Asp.Net的NHibernate网站程序,所以在Web.Config的configSections下增加section节点来配置NHibernate。也可以独立出hibernate.cfg.xml配置文件,放在网站Bin目录下,使用如下方式实例化:
Configuration config = new Configuration().Configure();
NHibernate默认会在目录下自动查找hibernate.cfg.xml配置文件。
如下为我的Web.Config配置文件内容:
<?xml version="1.0"?> <configuration> <configSections> <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /> </configSections> <appSettings/> <connectionStrings/> <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" > <session-factory name="NHibernateDemo"> <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> <property name="connection.connection_string"> Data Source=.\SQL2005;Initial Catalog=Jonllen; Integrated Security=True;Pooling=False </property> <property name="adonet.batch_size">10</property> <property name="show_sql">true</property> <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property> <property name="use_outer_join">true</property> <property name="command_timeout">10</property> <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property> <property name="proxyfactory.factory_class"> NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle </property> <mapping assembly="NHibernateDemo.Domain"/> </session-factory> </hibernate-configuration> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender, log4net"> <layout type="log4net.Layout.PatternLayout, log4net"> <param name="ConversionPattern" value="%d %p %m%n" /> </layout> </appender> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender,log4net" > <param name="File" value="log.txt" /> <param name="AppendToFile" value="true" /> <param name="DatePattern" value="yyyy.MM.dd" /> <layout type="log4net.Layout.PatternLayout,log4net"> <conversionPattern value="%d %p %m%n" /> </layout> </appender> <root> <priority value="DEBUG" /> <appender-ref ref="ConsoleAppender" /> </root> <logger name="NHibernate" additivity="false"> <level value="WARN"/> <appender-ref ref="RollingFile" /> <appender-ref ref="ConsoleAppender" /> </logger> <logger name="NHibernate.SQL" additivity="false"> <level value="ALL"/> <appender-ref ref="RollingFile" /> <appender-ref ref="ConsoleAppender" /> </logger> </log4net> <system.web> <compilation debug="true"> <assemblies> </assemblies> </compilation> <authentication mode="Forms"/> </system.web> </configuration>
三、映射
NHibernate提供了几种方式的实体关系映射,比较灵活的是使用xml文件进行映射,将实体类与数据表的关系描述配置。使用这种方式要指定NHibernate配置session-factory中mapping的assembly实体程序集,*.hbm.xml实体映射文件属性的生成操作都要设置为嵌入的资源,因为NHibernate是通过查找程序集中的资源文件映射实体的。
如下为我的User.hbm.xml映射文件内容:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateDemo.Domain" namespace="NHibernateDemo.Domain.Entities"> <class name ="User" table="bl_user" > <id name="UserId" column ="userId"> <generator class ="native"/> </id> <property name ="Username"/> <property name ="Password"/> <property name ="LoginTimes"/> <property name ="RegisterDate"/> <property name ="UserStatus"/> <property name ="TemplateDisplay"/> </class> </hibernate-mapping>
NHibernate也提供在编码时自定义映射方式,如使用Fluent在创建ISessionFactory时动态添加要映射的程序集。另外,我曾经在Hibernate中可以使用注解方式,即直接在实体类上添加注释来映射,我想在.Net中NHibernate应该也可以使用元数据进行实体映射。
四、延迟加载
NHibernate2.1版本提供三种3种框架动态代理,分别为Castle框架、LinFu框架、Spring.Net框架,以实现延迟加载功能。在我的这个NHibernate事例中是使用的Castle框架,它要求持久化类不是sealed密封类,且其公共方法、属性和事件声明必须为virtual,否则将出现如下错误:
The following types may not be used as proxies:NHibernateDemo.Domain.Entities.User: method get_UserId should be 'public/protected virtual' or 'protected internal virtual'
如果非得每个实体属性都要为virtual好象有点强人所难,于是我把User.hbm.xml中class的lazy="false",改为默认不使用延迟加载就没问题了。如果能正常使用延迟加载功能固然是好,但一般取数据实体里的属性一般都会是确定的,实在不行也可以在业务逻辑层里再手动填充实体属性,所以也不一定就要使用延迟加载。
目前.Net实现动态代理功能似乎都对现有的有侵入性,不是要求继承类就是有其它的实现条件。而之所以要有virtual这个条件是因为创建代理对象需要override实体类型的方法属性,以实现对方法属性的拦截处理,植入执行前和执行后的逻辑,以实现延迟加载的功能。而在Java中则专门提供了InvocationHandler代理接口,并且所有方法默认都可以Override,因此实现Aop动态代理就很简单。
本事例VS2005工程源代码下载:NHibernateDemo