目 录
NHibernate 是一个面向.NET 环境的对象/关系数据库映射工具。对象关系映射(O/R Mapping,Object Relational Mapping)表示一种技术,用来把对象模型表示的对象映射到基于SQL 的关系模型数据结构中去。
1)下载NHibernate-2.1.2.GA-bin.zip和NHibernate-2.1.2.GA-src.zip
2)新建名字为Nhibernate_map的Asp.net Web Application工程,导入nhibernate的相关引用,在NHibernate-2.1.2.GA-bin.zip文件夹下:
3)创建数据库表t_sys_Log,同时在工程里新建一个对应的映射类Loglist.cs,即映射类和配置文件里的字段属性与数据库表里的字段属性是一一对应的,包括名称和类型;以类型为String的UserName为例,如下图所示:
Ø 数据库表t_sys_Log:
Ø t_sys_Log表的Nhibernate映射类Loglist:
Ø t_sys_Log数据表所对应的配置文件
注意:
数据库的存放位置,跟映射无关。映射的形成主要由于映射类、映射文件.hbm.xml文件和config配置文件。在web工程中配置项直接放在web.config中;在WindowsFormsApplication中要添加App.config(具体:Add---AddItem—configuration file,默认名字为App.config)。配置文件的内容和功能是一样的。下面以文本.web.config为例:
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"/>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">
NHibernate.Dialect.MsSql2005Dialect
</property>
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Server=(local);Initial Catalog=csrcmap[1];
<!--User Id='123';Password='123456'-->
Integrated Security=True
</property>
<property name="proxyfactory.factory_class">
NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
该配置文件解析:
Ø <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"/>
该项添加到<configSections></configSections>之间;其余部分添加到<configuration></configSections>内的任意位置
Ø urn:nhibernate-configuration-2.2为nhibernate的版本号,在工程里的所有有关nhibernate的版本号必须一致,另一处在Loglist.hbm.xml里
Ø NHibernate.Dialect.MsSql2005Dialect:为数据库操作语言;本次使用的是sqlserver2005
Ø connection.driver_class:为对应的数据库驱动
Ø connection.connection_string:Server=(local);Initial Catalog=csrcmap:连接字符串;Server=127.0.0.1数据库服务器的IP地址;csrcmap为数据库的名字;Integrated Security=True这样的连接字符串是在数据库为windows验证登陆的情况下;否则,加上数据库sql登陆的用户名和登陆密码:User Id='用户名';Password='密码'
Ø proxyfactory.factory_class:nhibernate工厂代理,有三种:LinFu,castel等,选择哪一种要添加对应的引用
Ø 设置copy to output Directory 属性为copy aways
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Nhibernate_map"
namespace="Nhibernate_map">
<class name="Nhibernate_map. Loglist" table="t_sys_Log " lazy="false">
<id name="Id" column="LogonId" type="String" length="20">
<generator class="assigned" />
</id>
<property name="UserName" column= "UserName" type="String" length="500"/>
<property name="Ip" type="String" length="100"/>
<property name="Title" type="String" length="500"/>
<property name="UserDateTime" type="DateTime"/>
<property name="Memo" /> </class>
</hibernate-mapping>
注意事项:
Ø 名字必须是映射类名.hbm.xml
Ø Assembly和namespace对应的是映射类的命名空间的名字
Ø class name为全称,即命名空间.类名;table为数据库对应的类名;lazy="false" 是为了防止出错,具体什么作用不清楚
Ø 下面是映射类的属性和数据库表的一一对应关系,包括名称和类型的对应
Ø Loglist.hbm.xml的BuildAction属性设置为Embedded Resource
3、Nhibernate跨数据库
使用Nhibernate就是为了在不同数据库之间进行操作时,不用修改太多的程序代码,只需更改配置文件即可。在学习过程中,实现了Nhibernate在SQLserver2000、SQLserver2005和Oracle 10之间的转换。
3.1 SQLserver2000和SQLserver2005
Nhibernate持久层在SqLserver2000和Sqlserver2005数据库之间,要修改的代码较少;主要有一处:在Web.config中有这样一处配置<property name="dialect">
NHibernate.Dialect.MsSql2000Dialect
</property>
需要修改的只是将MsSql2000Dialect改为MsSql2005Dialect。其它的无需改变即可直接使用。
3.2 SQLserver和Oracle 10
因为SQLserver和oracle数据库自身存在的区别较大,当从SQLserver+Nhibernate转换为Oracle+Nhibernate时,需要修改的地方较多。
首先要说明的一点:Oracle数据库和表、视图、字段名称全部为大写;这一点比较容易受到迷惑。其实这样不影响HQL操作,因为在Nhibernate里面操作的是映射类,跟具体的数据表或字段的名称没关系,只需在创建映射类时一一对应即可。这一点在.hbm.xml文件中有所体现。
第二,因为Oracle和SQLserver的驱动类等存在不同,Oracle+Nhibernate的web.config应修改配置为:
<hibernate-configurationxmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<propertyname="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<propertyname="dialect">
NHibernate.Dialect.Oracle10gDialect
</property>
<propertyname="connection.driver_class">
NHibernate.Driver.OracleClientDriver
</property>
<propertyname="connection.connection_string">
Data Source=orcl201;User ID=nhb;Password=123456;Persist Security Info=true;Unicode=True
</property>
<!--连接Oracle -->
<propertyname="query.substitutions">
true 1, false 0, yes 'Y', no 'N'
</property>
<propertyname="proxyfactory.factory_class">
NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
</property>
<propertyname="show_sql">true</property>
</session-factory>
</hibernate-configuration>
第三、Oracle的数据类型;在SQLserver+Nhibernate时,可以很方便的将SQLserver的数据类型转换为HQL中的数据类型。但在Oracle+Nhibernate中,数据转换存在一些问题。所涉及到的一些数据类型中,最容易出错的是DataTime类型和System.Guid类型。
下面以这两个为例进行详细的说明:
首先DataTime类型,由于Nhibernate和Oracle内嵌的日期格式不一样,在直接进行使用时,一般会报错。改正这个问题的方法是在DataTime日期对象前面加上TO_DATE()进行转换,且TO_DATE一定要大写,Oracle严格区分大小写,小写时同样报错。例如在HQL语句中出现:USERDATETIME>= UserDateTimeBegin时,应将其改为 USERDATETIME >=TO_DATE('" + UserDateTimeBegin + "','YYYY-MM-DD')。同时,在修改程序代码时发现“Order by USERDATETIME ”在HQL中根本不起作用,得到的列表为乱序。
SQLserver和HQL中都存在System.Guid类型或是与其对应的类型,可以直接相互转换不出错。但是在Oracle数据库中没有直接可用的对应数据类型,网上一般推荐使用RAW类型或CHAR类型。RAW(16)可以跟Guid类型对应,但是数据库表里的值跟从数据库表里读出来显示到网页上的值不一样,在本次日志移植测试时,使用的VCHAR(50);也由于所使用的Oracle是从SQLserver2000数据库中转换而来的,在转换过程中也会报错,因此选择了VCHAR类型,即对应HQL里的String类型。就是将Guid类型转换为string字符串类型,传到数据库表中,再将其读出,即可得到一致的数值。这样在使用时也会出现一些问题,但基本是可用的,需要改进。
4、MyGeneration的简单使用
4.1 MyGeneration代码自动生成器
MyGeneration 是一款不错的ORM和代码生成工具,它基于模板(Template)工作,安装好MyGeneration 后自带了很多模板,并且提供在线模板库提供模板升级和允许用户自定义模板。MyGeneration 的模板可以用C#, VB.NET, JScript, and VBScript编写。使用MyGeneration 可以为Gentle.NET, Opf3, NHibernate等生成ORM架构或ORM文件,为多种数据库生成存储过程,为.Net项目生成C#、VB.NET 程序代码,PHP、HTML等页面代码。
本次使用MyGeneration主要是自动生成映射类和对应的.hbm.xml文件;特别是在数据库类较多的情况下,使用这款代码生成工具,可以大大的提高效率。简单使用步骤图示如下:
Ø 创建不同数据库的连接:
Ø 打开Template Browser和在线模板库
Ø 在线模板库里找到Nhibernate1.4,并将其下载
Ø 在Template Browser里找到Nhibernate1.4,右键执行
Ø 选择对应的数据库表或视图进行Map操作
Ø 在MyGeneration的安装目录下,有个名为GeneratedCode的文件夹,自动生成的映射实体类存放于该处
Ø 问题:在对视图进行Map时,只生成实体类,没有映射.hbm.xml文件,需要进一步研究
5、Nhibernate+增删改查
Ø from Loglist order by UserDateTime DESC
等价于SQL里:select * from LogList order by UserDateTime DESC
Ø delete from Loglist where ID = id
等价于:delete from LogList where ID=id
Ø from Loglist where ID = id
Ø select avg(c.CustomerId),sum(c.CustomerId),count(c) from Customer c
Ø Or语句
IList list = (IList)session.CreateCriteria(typeof(Loglist))
.Add
(
NHibernate.Criterion.Restrictions.Or
(
NHibernate.Criterion.Restrictions.Like("Title", "%" + Memo + "%"),
NHibernate.Criterion.Restrictions.Like("Memo", "%" + Memo + "%")
)
)
.AddOrder(Order.Desc("UserDateTime"))
.List<Loglist>() as IList;
注意:所有的HQL操作的都是映射类,不是数据库表,上面的Loglist是映射类名,不是数据库表的名称。HQL语句严格区分大小写。
Nhibernate在执行时操作的是类的对象,在下文使用到的代码均为从oracle+Nhibernate里拷贝过来的,所以会有大小写的不同:
插入时,即插入一个对象;因此要先新建一个对象,对对象的各个属性字段进行赋值;然后保存session。
代码:
//gepeipei 2011-5-23
T_SYS_LOG log = new T_SYS_LOG();
log.ID = id;
log.IP = Ip;
log.USERNAME = UserName;
log.TITLE = Title;
log.MEMO = content;
log.USERDATETIME = DateTime.Now;
//gepeipei 2011-5-26
session.Save(log);
session。Flush()
tran.Commit();
删除时;可以对一个对象进行直接删除,即session.delete(对象);也可以执行HQL语句,如:delete 字段名 from 表名 where 字段=“”;执行完HQL语句之后,要进行session更新操作,否则数据库表的数据不变。
法一:
//获取一个符合条件的T_SYS_DATA对象
T_SYS_DATA dt = data.GetModel(ID);
session.Delete(dt);
session.Flush();
tran.Commit();
法二:
string str_sql = "";
//NHibernate语句
str_sql = " delete from T_SYS_LOG ";
str_sql += " WHERE ID = '" + ID + "' ";
//执行sql语句,在进行删除操作时,需要执行更新操作,否则删除结果不受影响
IQuery query = session.CreateQuery(str_sql);
int row = query.ExecuteUpdate();
修改时:在证监会里用到了编辑操作和用户密码修改,这两个都属于对数据库对象的修改。在这里没有使用update更新语句,而是根据给出的条件获取一个新的对象,像根据ID,得到一个T_SYS_DATA的对象,在对于这个对象的部分属性进行修改;最后以 session.Saveorupdate();
session.Flush();
Tran.Commit(),进行更新。
代码:
T_SYS_DATA data = data_1.GetModel(ID);
data.DATAMC = DataMc;
data.DISPLAYORDER = DisplayOrder;
data.ISUSE = IsUse;
data.ISVISIBLE = IsVisible;
session.SaveOrUpdate(data);
session.Flush();
tran.Commit();
查询时:相对比较简单,HQL语句跟sqlserver语句基本一致,就是在oracle里要把表名和属性字段改为大写。执行lqy= session.CreateQuery(Sqlstr);将查询结果赋值给list,就可以将其取出使用
5.3、多条件查询
HQL多条件查询时,支持Or、And等连接的HQL语句,出错概率也比较大,因此推荐使用较正规点的ICriteria进行多条件的查询
利用ICriteria进行条件查询;session.CreateCriteria(typeof(Loglist)
//Ge,Le,Eq对应于大于小于等于;返回值为IList类型,
list_log = (IList )session.CreateCriteria(typeof(Loglist))
.Add(Restrictions.Ge("UserDateTime", Convert.ToDateTime(UserDateTimeBegin)))
.Add(Restrictions.Le("UserDateTime", Convert.ToDateTime(UserDateTimeEnd)))
.SetFirstResult(AspNetPager1.PageSize * (AspNetPager1.CurrentPageIndex - 1))//从哪条记录开始
.SetMaxResults(AspNetPager1.PageSize)//总共多少条记录
.AddOrder(Order.Desc("UserDateTime"))
.List<Loglist>();
nhibernate里面最常用的就是IList类型,但Ilist类型有个缺点就是不排序,如果要进行数据绑定的话,需要将其转换为DataTable,再将其绑定
public DataTable ToDataTable(IList list)
{
DataTable result = new DataTable();
if (list.Count > 0)
{
PropertyInfo[] propertys = list[0].GetType().GetProperties();
foreach (PropertyInfo pi in propertys)
{
result.Columns.Add(pi.Name, pi.PropertyType);
}
for (int i = 0; i < list.Count; i++)
{
ArrayList tempList = new ArrayList();
foreach (PropertyInfo pi in propertys)
{
object obj = pi.GetValue(list[i], null);
tempList.Add(obj);
}
object[] array = tempList.ToArray();
result.LoadDataRow(array, true);
}
}
return result;
}
Ø not mapped
出错点:有可能是hql语句操作的不是映射类的名字,而是表的名字;
或者是.hbm.xml文件的buildaction没有设置为embedded resource
Ø 未能从程序集“Nhibernate_map, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中加载类型“Nhibernate_map.11”
cfg.AddAssembly("Nhibernate_map");报错
出错点:.hbm.xml文件中的class name所对应的值不是映射类的全称,或者是名字不对
Ø Could not find schema information for the element 'urn:nhibernate-mapping-2.0:hibernate-mapping'
出错点:版本号不对,或者是两个文件中的版本号不一致(web.config和.hbm.xml)
Ø "Could not find the dialect in the configuration"异常
出错点:config文件中有关数据库驱动和数据库语言配置跟所使用的数据库不一致引起的
Ø 提示添加<property name="proxyfactory.factory_class"> NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu </property>
<property name="proxyfactory.factory_class">
NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle </property>的异常
出错点:引用不全,添加LinFu或Castle 的引用,这是两种工厂创建方式,选其一即可,添加对应的引用
Ø 引发类型为“Antlr.Runtime.NoViableAltException”的异常。
出错点:HQL语句出现语法错误
有关nhibernate使用过程中,大部分出错源于配置文件的设置(版本号、数据库名和映射类名,嵌入资源),及映射类和数据库表Mapping关系,还有就是要记住nhibernate里的HQL语句操作的是映射类的对象,不是数据库表,这是主要的区别,因为操作的是映射类,所以是面向对象的,脱离了单纯的数据库表的操作,提供了一个.net和数据库之间的部件,不同的数据库只要数据配置文件里设置的驱动不同即可实现连接和映射。