一、NHibernate的引入
至于为何要在项目中使用NHibernate不是这里讨论的问题,在 http://sourceforge.net/projects/nhibernate 可以下到最新版的NHibernate包及其源代码。(写这篇文章时NHibernate仍处于Beta测试阶段,相关文档也仅仅是对Java中的Hibernate文档进行的拷贝,不过目前已经有了1.0的正式版)
将下载的压缩包(nhibernate-0.7.0.0)解压后会得到几个目录
bin --- 最重要的!NHibernate及其依赖的程序集,和一些配置文件的实例
clover --- 针对 NHibernate 的 Clover Report
doc\sdk --- NHibernate的类文档,类似与Java的javadoc,使用NHibernate最根本的参考资料!
lib --- NHibernate中使用到的类库(应该和bin中的依赖程序集一样的)
src --- NHibernate及其例子的所有源代码
testresults --- 用NUnit对NHibernate及其例子进行单元测试的结果
当要使用NHibernate时,需要在我们的工程中引入bin目录下的NHibernate.dll,log4net.dll,Iesi.Collections.dll,Castle.DynamicProxy.dll,HashCodeProvider.dll这五个程序集,第一个是其核心的类库,后面四个是其依赖程序集;另有一个nunit.framework.dll用于单元测试,若用到也需要引入。
二、全局性配置
NHibernate的配置主要包括两部分:全局性配置和映射文件
在几乎所有的开源Framework里我们几乎都能碰到这样的全局性配置,就是对整个Framework的运行环境和一些影响全局行为的默认参数进行设置,只不过有些是配置在Web.xml里,而有些是单独的配置文件。
在J2EE中,我们通常用一个独立的hibernate.cfg.xml来配置,但在ASP.NET中我们有可以灵活扩展的全局性Web.config文件,更加方便:
1.首先,需要在<configSections>部分生命nhibernate的配置节,如下:
<section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.3300.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
后面的Version,Culture,PublicKeyToken值可以从你下载的NHibernate.dll通过反编译工具获得,如Reflector
2.上一步相信都知道是什么目的,接下来就是对nhibernate进行具体的配置,下面是一个具体的例子:
<nhibernate>
<add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
<add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect"/>
<add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>
<add key="hibernate.connection.connection_string" value="Server=SCS-TPC;initial catalog=CMSData;User id=sa;password=sa;"/>
<add key="hibernate.show_sql" value="true"/>
</nhibernate>
这是使用NHibernate自己管理的数据源的最简单配置,解释如下:
hibernate.connection.provider —— 自定义的ConnectionProvider类名,此类用来向nhibernate提供JDBC连接
hibernate.connection.driver_class —— 数据库连接的驱动类
hibernate.connection.connection_string —— 数据库连接字符串,包括主机名、数据库名、用户名、密码,注意:很多实际项目中处于安全性,会将该连接字符串写入注册表中,那么该参数就只能在程序中动态赋值了
hibernate.dialect —— 数据库方言类,nhibernate根据不同的方言来适应不同的数据库,到0.7版只提供了支持MsSql2000的方言,不知道正式的1.0版是否更丰富了
hibernate.show_sql —— 是否在日志中输出hibernate生成的实际Sql语句
事实上还有很多配置参数,详细的请参考下载包的doc部分和Java的hibernate_reference。
另外还可以和Java中一样使用一个独立的hibernate.cfg.xml来配置全局性变量,下面是一个配置的实例,不详述:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
" http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="show_sql">true</property>
<!--Use the hibernate connection-->
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Server=SCS-TPC;initial catalog=CMSData
</property>
<property name="connection.username">sa</property>
<property name="connection.password">sa</property>
<property name="dialect">
NHibernate.Dialect.MsSql2000Dialect
</property>
<mapping resource="hibernate/user.hbm.xml" />
<mapping resource="hibernate/cards.hbm.xml" />
<mapping resource="hibernate/cardgroup.hbm.xml" />
</session-factory>
</hibernate-configuration>
创建实体层的类和相应的映射文件(可用Cool Coder等工具生成)
三、创建实体层的类
nhibernate的一个最重要目的就是实现关系型数据库表和面向对象的实体类之间的映射,应此实体类就是hibernate操作的具体内容。
nhibernate中定义的实体就是一个标准的JavaBean,不需要继承或实现任何基类或接口,对每一个属性都有对象的Getter/Setter存取方法。
using System;
using System.Collections;
namespace TPC.Hibernate.Entity
{
/// <summary>
/// UserBE 的摘要说明。
/// </summary>
public class UserBE
{
# region "private members"
private Int32 id;
private string userName;
private string password;
private string email;
private IList roles = null;
# endregion
# region "public methods"
public IList Roles
{
get
{
return roles;
}
set
{
roles = value;
}
}
public string Email
{
get
{
return email;
}
set
{
email = value;
}
}
public Int32 Id
{
get
{
return id;
}
set
{
id = value;
}
}
public string UserName
{
get
{
return userName;
}
set
{
userName = value;
}
}
public string Password
{
get
{
return password;
}
set
{
password = value;
}
}
# endregion
}
}
三、定义映射文件(*.hbm.xml)
对象和关系数据库之间的映射通常是用一个XML文档(XML document)来定义的。
请注意,虽然很多Hibernate用户选择手写XML映射文档,但也有一些工具可以用来生成映射文档,如Cool Coder等工具。
让我们从一个映射的例子开始:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="TPC.Hibernate.Entity.Authentication.UserBE, TPC.Hibernate" table="TB_CmsUser">
<id name="Id" column="Id" type="Int32">
<generator class="identity" />
</id>
<property name="UserId" type="String(12)" column="UserId" />
<property name="Password" type="String(100)" column="Password" />
<property name="UserName" type="String(30)" column="Name" />
<property name="CompanyId" type="Int32" column="CompanyId" />
<property name="Email" type="String(50)" column="Email" />
<property name="UserStatus" type="Int32" column="Status" />
<property name="LastLogin" type="DateTime" column="LastLogin" />
<property name="IsFirstTimeLogin" type="Boolean" column="IsFirstLogin" />
<property name="LastPwdChange" type="DateTime" column="LastPwdChange" />
<property name="LoginFail" type="Int32" column="LoginFail" />
<bag name="Roles" table="TB_CmsUserRole" inverse="false">
<key column="UserId"/>
<one-to-many class="TPC.Hibernate.Entity.UserRoleBE,TPC.Hibernate"/>
</bag>
</class>
</hibernate-mapping>
1.hibernate-mapping
这个元素包括一些可选的属性。schema和catalog属性, 指明了这个映射所连接(refer)的表所在的schema和/或catalog名称。 假若指定了这个属性,表名会加上所指定的schema和catalog的名字扩展为全限定名。假若没有指定,表名就不会使用全限定名。 default-cascade指定了未明确注明cascade属性的Java属性和 集合类Hibernate会采取什么样的默认级联风格。auto-import属性默认让我们在查询语言中可以使用 非全限定名的类名。
<hibernate-mapping
schema="schemaName" (1)
catalog="catalogName" (2)
default-cascade="cascade_style" (3)
default-access="field|property|ClassName" (4)
default-lazy="true|false" (5)
auto-import="true|false" (6)
package="package.name" (7)
/>
(1) schema (可选): 数据库schema的名称。
(2) catalog (可选): 数据库catalog的名称。
(3) default-cascade (可选 - 默认为 none): 默认的级联风格。
(4) default-access (可选 - 默认为 property): Hibernate用来访问属性的策略。可以通过实现PropertyAccessor接口 自定义。
(5) default-lazy (可选 - 默认为 true): 指定了未明确注明lazy属性的Java属性和集合类, Hibernate会采取什么样的默认加载风格。
(6) auto-import (可选 - 默认为 true): 指定我们是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。
(7) package (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。
注意hibernate-mapping 元素允许你嵌套多个如上所示的 <class>映射。但是最好的做法(也许一些工具需要的)是一个 持久化类(或一个类的继承层次)对应一个映射文件,并以持久化的超类名称命名,例如: Cat.hbm.xml, Dog.hbm.xml,或者如果使用继承,Animal.hbm.xml。
2.class
你可以使用class元素来定义一个持久化类:
<class
name="ClassName" (1)
table="tableName" (2)
discriminator-value="discriminator_value" (3)
mutable="true|false" (4)
schema="owner" (5)
catalog="catalog" (6)
proxy="ProxyInterface" (7)
dynamic-update="true|false" (8)
dynamic-insert="true|false" (9)
select-before-update="true|false" (10)
polymorphism="implicit|explicit" (11)
where="arbitrary sql where condition" (12)
persister="PersisterClass" (13)
batch-size="N" (14)
optimistic-lock="none|version|dirty|all" (15)
lazy="true|false" (16)
entity-name="EntityName" (17)
check="arbitrary sql check condition" (18)
rowid="rowid" (19)
subselect="SQL expression" (20)
abstract="true|false" (21)
entity-name="EntityName" (22)
node="element-name" (23)
/>
(1) name (可选): 持久化类(或者接口)的Java全限定名。 如果这个属性不存在,Hibernate将假定这是一个非POJO的实体映射。
(2) table (可选 - 默认是类的非全限定名): 对应的数据库表名。
(3) discriminator-value (可选 - 默认和类名一样): 一个用于区分不同的子类的值,在多态行为时使用。它可以接受的值包括 null 和 not null。
(4) mutable (可选,默认值为true): 表明该类的实例是可变的或者可变的。
(5) schema (可选): 覆盖在根<hibernate-mapping>元素中指定的schema名字。
(6) catalog (可选): 覆盖在根<hibernate-mapping>元素中指定的catalog名字。
(8) dynamic-update (可选, 默认为 false): 指定用于Update 的SQL将会在运行时动态生成,并且只更新那些改变过的字段。
(9) dynamic-insert (可选, 默认为 false): 指定用于Insert的 SQL 将会在运行时动态生成,并且只包含那些非空值字段。
(10) select-before-update (可选, 默认为 false): 指定Hibernate除非确定对象真正被修改了(如果该值为true-译注),否则不会执行SQL Update操作。在特定场合(实际上,它只在一个瞬时对象(transient object)关联到一个 新的session中时执行的update()中生效),这说明Hibernate会在Update 之前执行一次额外的SQL Select操作,来决定是否应该执行 Update。
(11) polymorphism(多态) (可选, 默认值为 implicit (隐式) ): 界定是隐式还是显式的使用多态查询(这只在Hibernate的具体表继承策略中用到-译注)。
(12) where (可选) 指定一个附加的SQLWhere 条件, 在抓取这个类的对象时会一直增加这个条件。
(13) persister (可选): 指定一个定制的ClassPersister。
(14) batch-size (可选,默认是1) 指定一个用于 根据标识符(identifier)抓取实例时使用的"batch size"(批次抓取数量)。
(15) optimistic-lock(乐观锁定) (可选,默认是version): 决定乐观锁定的策略。
(16) lazy (optional): 通过设置lazy="false", 所有的延迟加载(Lazy fetching)功能将未被激活(disabled)。
(18) check (可选): 这是一个SQL表达式, 用于为自动生成的schema添加多行(multi-row)约束检查。
(19) rowid (可选): Hibernate可以使用数据库支持的所谓的ROWIDs,例如: oracle数据库,如果你设置这个可选的rowid, Hibernate可以使用额外的字段rowid实现快速更新。ROWID是这个功能实现的重点, 它代表了一个存储元组(tuple)的物理位置。
(20) subselect (可选): 它将一个不可变(immutable)并且只读的实体映射到一个数据库的 子查询中。它用于实现一个视图代替一张基本表,但是最好不要这样做。
(22) entity-name (可选, 默认为类名): 显式指定实体名
注意:若指明的持久化类实际上是一个接口,这也是完全可以接受的。 之后你可以用<subclass>来指定该接口的实际实现类。
你可以持久化任何static(静态的)内部类,你应该使用标准的类名格式来指定类名,比如:Foo$Bar。
不可变类,mutable="false"不可以被应用程序更新或者删除。 这可以让Hibernate做一些小小的性能优化。
请注意dynamic-update和dynamic-insert的设置并不会继承到子类, 所以在<subclass>或者<joined-subclass>元素中可能 需要再次设置。这些设置是否能够提高效率要视情形而定。请用你的智慧决定是否使用。
使用select-before-update通常会降低性能。如果你重新连接一个脱管(detache)对象实例 到一个Session中时,它可以防止数据库不必要的触发update。 这就很有用了。
如果你打开了dynamic-update,你可以选择几种乐观锁定的策略:
version(版本检查) 检查version/timestamp字段
all(全部) 检查全部字段
dirty(脏检查)只检察修改过的字段
none(不检查)不使用乐观锁定
我们非常强烈建议你在Hibernate中使用version/timestamp字段来进行乐观锁定。 对性能来说,这是最好的选择,并且这也是唯一能够处理在session外进行操作的策略。
3.id
被映射的类必须定义对应数据库表主键字段。大多数类有一个JavaBeans风格的属性, 为每一个实例包含唯一的标识。<id> 元素定义了该属性到数据库表主键字段的映射。
<id
name="propertyName" (1)
type="typename" (2)
column="column_name" (3)
unsaved-value="null|any|none|undefined|id_value" (4)
access="field|property|ClassName" (5)
node="element-name|@attribute-name|element/@attribute|.">
<generator class="generatorClass"/>
</id>
(1) name (可选): 标识属性的名字。
(2) type (可选): 标识Hibernate类型的名字。
(3) column (可选 - 默认为属性名): 主键字段的名字。
(4) unsaved-value (可选 - 默认为一个字段判断(sensible)的值): 一个特定的标识属性值,用来标志该实例是刚刚创建的,尚未保存。 这可以把这种实例和从以前的session中装载过(可能又做过修改--译者注) 但未再次持久化的实例区分开来。
(5) access (可选 - 默认为property): Hibernate用来访问属性值的策略。
如果 name属性不存在,会认为这个类没有标识属性。
unsaved-value 属性很重要!如果你的类的标识属性不是默认为 正常的Java默认值(null或零),你应该指定正确的默认值。
还有一个另外的<composite-id>定义可以访问旧式的多主键数据。 我们强烈不建议使用这种方式。
4.Generator
可选的<generator>子元素是一个Java类的名字, 用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数, 用<param>元素来传递。
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
所有的生成器都实现net.sf.hibernate.id.IdentifierGenerator接口。 这是一个非常简单的接口;某些应用程序可以选择提供他们自己特定的实现。当然, Hibernate提供了很多内置的实现。下面是一些内置生成器的快捷名字:
increment: 用于为long, short或者int类型生成 唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。
identity: 对DB2,MySQL,MS SQL Server,Sybase和HypersonicSQL的内置标识字段提供支持。 返回的标识符是Int32, Int64 或者Int16类型的。
sequence: 在DB2,PostgreSQL, oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的标识符是long, short或者 int类型的。
hilo: 使用一个高/低位算法高效的生成long, short 或者 int类型的标识符。
seqhilo: 使用一个高/低位算法来高效的生成long, short 或者 int类型的标识符,给定一个数据库序列(sequence)的名字。
uuid: 用一个128-bit的UUID算法生成字符串类型的标识符, 这在一个网络中是唯一的(使用了IP地址)。UUID被编码为一个32位16进制数字的字符串。
guid: 在MS SQL Server 和 MySQL 中使用数据库生成的GUID字符串。
native: 根据底层数据库的能力选择identity, sequence 或者hilo中的一个。
assigned: 让应用程序在save()之前为对象分配一个标示符。这是 <generator>元素没有指定时的默认生成策略。
select: 通过数据库触发器选择一些唯一主键的行并返回主键值来分配一个主键。
foreign: 使用另外一个相关联的对象的标识符。通常和<one-to-one>联合起来使用。
5.property
<property>元素为类定义了一个持久化的,JavaBean风格的属性。
<property
name="propertyName" (1)
column="column_name" (2)
type="typename" (3)
update="true|false" (4)
insert="true|false" (4)
formula="arbitrary SQL expression" (5)
access="field|property|ClassName" (6)
lazy="true|false" (7)
unique="true|false" (8)
not-null="true|false" (9)
optimistic-lock="true|false" (10)
node="element-name|@attribute-name|element/@attribute|."
/>
(1) name: 属性的名字
(2) column (可选 - 默认为属性名字): 对应的数据库字段名。 也可以通过嵌套的<column>元素指定。
(3) type (可选): 一个Hibernate类型的名字。
(4) update, insert (可选 - 默认为 true) : 表明用于Update 和/或 Insert 的SQL语句中是否包含这个被映射了的字段。这二者如果都设置为false 则表明这是一个“外源性(derived)”的属性,它的值来源于映射到同一个(或多个) 字段的某些其他属性,或者通过一个trigger(触发器)或其他程序。
(5) formula (可选): 一个SQL表达式,定义了这个计算 (computed) 属性的值。计算属性没有和它对应的数据库字段。
(6) access (可选 - 默认值为 property): Hibernate用来访问属性值的策略。
(7) lazy (可选 - 默认为 false): 指定 指定实例变量第一次被访问时,这个属性是否延迟抓取(fetched lazily)( 需要运行时字节码增强)。
(8) unique (可选): 使用DDL为该字段添加唯一的约束。 此外,这也可以用作property-ref的目标属性。
(9) not-null (可选): 使用DDL为该字段添加可否为空(nullability)的约束。
(10) optimistic-lock (可选 - 默认为 true): 指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。
typename可以是如下几种:
Hibernate基础类型之一(比如:integer, string, character,date, timestamp, float, binary, serializable, object, blob)。
一个类的名字,这个类属于一种默认基础类型 。
一个自定义类型的类的名字。(比如: com.illflow.type.MyCustomType)。
关于NHibernate的映射太过复杂了,尤其是要想利用一些高级特性时,比如nhibernate的关联关系,子类等,不详述了,干脆贴个参考文档在这里算了,Java的hibernate_reference,有超详细的说明:) http://www.hibernate.org/hib_docs/v3/reference/zh-cn/html/
公用的数据层代码,如对SessionFactory等公用的操作进行封装
六、公用的数据层代码和NHibernate帮助类
在实际操作NHibernate进行数据库操作时,我们通常需要自己定义帮助类来操作hibernate,以下是我在项目中定义的一系列帮助类,是的普通开发人员即使在对Hibernate不够熟悉的情况也可以通过它来完成开发。
1.HibernateSession类,从NHibernate的SessionFactory获取或释放Session的核心类
using System;
using System.Threading;
using System.Reflection;
using NHibernate;
using NHibernate.Cfg;
using TPC.Common.Exceptions;
using TPC.Common.Utilities;
namespace TPC.Hibernate.Core
{
/// <summary>
/// HibernateSession 的摘要说明。
/// </summary>
public class HibernateSession
{
private static ISessionFactory sessionFactory;
private static LocalDataStoreSlot slot = Thread.GetNamedDataSlot("HibernateSession");
public static ISession currentSession()
{
Object session = Thread.GetData(slot);
//若没有可用 Session,则打开一个新的
if(session == null)
{
if (sessionFactory == null)
{
try
{
Configuration cfg = new Configuration();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
sessionFactory = cfg.BuildSessionFactory();
}
catch (HibernateException ex)
{
Logger.Fatal(typeof(HibernateSession), ex.Message, ex);
throw new NHibernateException(ex, "Fatal error occured, please contact the administrator!", "04 001");
}
}
try
{
session = sessionFactory.OpenSession();
}
catch(HibernateException ex)
{
Logger.Fatal(typeof(HibernateSession), ex.Message, ex);
throw new NHibernateException(ex, "Fatal error occured, Can not connect the database!", "04 002");
}
Thread.SetData(slot, session);
}
return (ISession)session;
}
public static void closeSession()
{
ISession session = (ISession) Thread.GetData(slot);
Thread.SetData(slot, null);
try
{
if(session != null)
session.Close();
}
catch(HibernateException ex)
{
Logger.Fatal(typeof(HibernateSession), "Can not disconnect the database, please check the hibernate configuration and network connection!", ex);
throw new NHibernateException(ex, "Fatal error occured, Can not disconnect the database!", "04 003");
}
}
}
}
2.QueryGenerator类,重新包装了通过Session获取Query,并设置参数的方法
using System;
using System.Collections;
using NHibernate;
using TPC.Common.Exceptions;
using TPC.Common.Utilities;
namespace TPC.Hibernate.Core
{
/// <summary>
/// QueryGenerator 的摘要说明。
/// </summary>
public class QueryGenerator
{
public static IQuery createQuery(ISession session, String sqlStr)
{
return createQuery(session, sqlStr, null);
}
public static IQuery createQuery(ISession session, String sqlStr, IDictionary param)
{
try
{
IQuery query = session.CreateQuery(sqlStr);
string[] NamedParams = query.NamedParameters;
if (NamedParams != null && NamedParams.Length != 0)
{
for (int i=0; i<NamedParams.Length; i++)
{
string name = NamedParams[i];
setParameter(query, name, param[name]);
}
}
return query;
}
catch(Exception ex)
{
Logger.Error(typeof(QueryGenerator), ex.Message, ex);
throw new NHibernateException(ex, "04 090");
}
}
public static IQuery createQuery(ISession session, String sqlStr, Object bean)
{
try
{
IQuery query = session.CreateQuery(sqlStr);
if (bean != null)
{
query.SetProperties(bean);
}
return query;
}
catch(Exception ex)
{
Logger.Error(typeof(QueryGenerator), ex.Message, ex);
throw new NHibernateException(ex, "04 091");
}
}
private static void setParameter(IQuery query, string name, Object obj)
{
query.SetParameter(name, obj);
//若存在Hibernate无法自己猜测出obj类型的情况,
//这里需要对特别类型的参数进行特别的赋值
}
}
}
3.DBHelper类,在程序中使用NHibernate时实际调用的类,包括了所有调用底层数据库操作的方法
using System;
using System.Data;
using System.Collections;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.SqlCommand;
using NHibernate.Engine;
using TPC.Common.Exceptions;
using TPC.Common.Utilities;
namespace TPC.Hibernate.Core
{
/// <summary>
/// DBHelper 的摘要说明。
/// </summary>
public class DBHelper
{
#region methods for inner transaction
public static IList getList(String sqlStr)
{
return getList(sqlStr, null);
}
public static IList getList(String sqlStr, IDictionary param)
{
ISession session = HibernateSession.currentSession();
//ITransaction tx = session.BeginTransaction();
IList result = null;
try
{
IQuery query = QueryGenerator.createQuery(session, sqlStr, param);
result = query.List();
//tx.Commit();
}
catch (HibernateException ex)
{
//tx.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 011");
}
finally
{
release();
}
return result;
}
public static IList getList(String sqlStr, Object bean)
{
ISession session = HibernateSession.currentSession();
//ITransaction tx = session.BeginTransaction();
IList result = null;
try
{
IQuery query = QueryGenerator.createQuery(session, sqlStr, bean);
result = query.List();
//tx.Commit();
}
catch (HibernateException ex)
{
//tx.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 012");
}
finally
{
release();
}
return result;
}
public static Object loadObject(Object obj, Object id)
{
ISession session = HibernateSession.currentSession();
Object result = null;
try
{
session.Load(obj, id);
result = obj;
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 021");
}
finally
{
release();
}
return result;
}
public static Object refreshObject(Object obj)
{
ISession session = HibernateSession.currentSession();
Object result = null;
try
{
session.Refresh(obj);
result = obj;
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 022");
}
finally
{
release();
}
return result;
}
public static void insertObject(Object obj)
{
ISession session = HibernateSession.currentSession();
ITransaction tx = session.BeginTransaction();
try
{
insert(session, obj);
tx.Commit();
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 031");
}
finally
{
release();
}
}
public static void updateObject(Object obj)
{
ISession session = HibernateSession.currentSession();
ITransaction tx = session.BeginTransaction();
try
{
update(session, obj);
tx.Commit();
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 032");
}
finally
{
release();
}
}
public static void deleteObject(Object obj)
{
ISession session = HibernateSession.currentSession();
ITransaction tx = session.BeginTransaction();
try
{
delete( session, obj );
tx.Commit();
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 033");
}
finally
{
release();
}
}
public static void deleteObject(String sqlStr, IDictionary param)
{
ISession session = HibernateSession.currentSession();
ITransaction tx = session.BeginTransaction();
IList list = getListWithoutRelease(sqlStr, param);
int count = list.Count;
try
{
for( int i = 0; i < count; i++ )
{
delete( session, list[ i ] );
}
tx.Commit();
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 034");
}
finally
{
release();
}
}
public static void executeBatchWithTransaction(IList objs, IList actypes)
{
ISession session = HibernateSession.currentSession();
ITransaction tx = session.BeginTransaction();
try
{
for (int i = 0; i < objs.Count; i++)
{
if(actypes[i].Equals("save"))
session.Save(objs[i]);
else if(actypes[i].Equals("update"))
session.Update(objs[i]);
else if(actypes[i].Equals("delete"))
session.Delete(objs[i]);
}
tx.Commit();
}
catch (HibernateException ex)
{
tx.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 035");
}
finally
{
release();
}
}
public static ISession getSession()
{
ISession session = HibernateSession.currentSession();
Logger.Info(typeof(DBHelper), "The hibernate session has been retrieved.");
return session;
}
public static void release()
{
HibernateSession.closeSession();
Logger.Info(typeof(DBHelper), "The hibernate session has been released.");
}
#endregion
#region methods for outer transaction
public static ISession beginTransaction()
{
ISession session = HibernateSession.currentSession();
try
{
session.BeginTransaction();
}
catch(HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 041");
}
return session;
}
public static IList getListWithoutRelease(String sqlStr, IDictionary param)
{
ISession session = HibernateSession.currentSession();
//ITransaction tx = session.BeginTransaction();
IList result = null;
try
{
IQuery query = QueryGenerator.createQuery(session, sqlStr, param);
result = query.List();
//tx.Commit();
}
catch (HibernateException ex)
{
//tx.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 013");
}
return result;
}
public static IList getListWithoutRelease(ISession session, String sqlStr, IDictionary param)
{
IList result = null;
try
{
IQuery query = QueryGenerator.createQuery(session, sqlStr, param);
result = query.List();
//tx.Commit();
}
catch (HibernateException ex)
{
//tx.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 013");
}
return result;
}
public static Object load(ISession session,Object obj, Object id)
{
Object result = null;
try
{
session.Load(obj, id);
result = obj;
}
catch (HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 055");
}
return result;
}
public static void insert(ISession session, Object obj)
{
try
{
session.Save(obj);
}
catch (HibernateException ex)
{
session.Transaction.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 051");
}
}
public static void update(ISession session, Object obj)
{
try
{
session.Update(obj);
}
catch (HibernateException ex)
{
session.Transaction.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 052");
}
}
public static void delete(ISession session, Object obj)
{
try
{
session.Delete(obj);
}
catch (HibernateException ex)
{
session.Transaction.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 053");
}
}
public static int delete(ISession session, String hql, IDictionary param)
{
IList list = getListWithoutRelease(hql, param);
int count = list.Count;
try
{
for( int i = 0; i < count; i++ )
{
delete( session, list[ i ] );
}
}
catch (HibernateException ex)
{
session.Transaction.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 054");
}
return count;
}
public static void endTransaction(ISession session)
{
ITransaction tx = session.Transaction;
try
{
if(!tx.WasRolledBack)
tx.Commit();
}
catch(HibernateException ex)
{
tx.Rollback();
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 042");
}
finally
{
release();
}
}
public static void rollBack(ISession session)
{
ITransaction tx = session.Transaction;
try
{
tx.Rollback();
}
catch(HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 043");
}
}
#endregion
/// <summary>
/// Added by Luo Li Juan, modified by Alfred.
/// The following methods use native sql as input parameter.
/// They are not recommended
/// </summary>
#region execute native sql
public static IList executeSQL(string sql)
{
IList result = new ArrayList();
ISessionFactoryImplementor sf;
IDbCommand cmd;
IDbConnection conn;
try
{
Configuration cfg = new Configuration();
sf = (ISessionFactoryImplementor)cfg.BuildSessionFactory();
cmd = sf.ConnectionProvider.Driver.CreateCommand();
cmd.CommandText = sql;
conn = sf.OpenConnection();
}
catch(HibernateException ex)
{
Logger.Fatal(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "Fatal error occured, Can not connect the database!", "04 004");
}
try
{
cmd.Connection = conn;
IDataReader rs = cmd.ExecuteReader();
while(rs.Read())
{
Array subList=new object[rs.FieldCount];
for(int i = 0; i < rs.FieldCount; i ++)
{
subList.SetValue(rs.GetValue(i),i);
}
result.Add(subList);
}
}
catch(HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 061");
}
finally
{
sf.CloseConnection(conn);
}
return result;
}
public static bool executeNonSQL(string sql)
{
bool result = false;
ISessionFactoryImplementor sf;
IDbCommand cmd;
IDbConnection conn;
try
{
Configuration cfg = new Configuration();
sf = (ISessionFactoryImplementor)cfg.BuildSessionFactory();
cmd = sf.ConnectionProvider.Driver.CreateCommand();
cmd.CommandText = sql;
conn = sf.OpenConnection();
}
catch(HibernateException ex)
{
Logger.Fatal(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "Fatal error occured, Can not connect the database!", "04 005");
}
try
{
cmd.Connection = conn;
cmd.ExecuteNonQuery();
result = true;
}
catch(HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 062");
}
finally
{
sf.CloseConnection(conn);
}
return result;
}
#endregion
/// <summary>
/// Added by Alfred
/// The following methods are used to execute stored procedure
/// They can only be used in WinService
/// </summary>
#region execute stored procedure
public static IList executeStoredProcedure(string spname, IDictionary param)
{
IList result = new ArrayList();
ISessionFactoryImplementor sf;
IDbCommand cmd;
IDbConnection conn;
try
{
/// <description>Building up Hibernate Configuration</description>
Configuration cfg = new Configuration();
sf = (ISessionFactoryImplementor)cfg.BuildSessionFactory();
cmd = sf.ConnectionProvider.Driver.CreateCommand();
cmd.CommandText = spname;
cmd.CommandType = CommandType.StoredProcedure;
PopulateParameters(cmd, param);
conn = sf.OpenConnection();
}
catch(HibernateException ex)
{
Logger.Fatal(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "Fatal error occured, Can not connect the database!", "04 004");
}
try
{
cmd.Connection = conn;
IDataReader rs = cmd.ExecuteReader();
/// <description>Convert Result Set to IList</description>
while(rs.Read())
{
Array subList=new object[rs.FieldCount];
for(int i = 0; i < rs.FieldCount; i ++)
{
subList.SetValue(rs.GetValue(i),i);
}
result.Add(subList);
}
}
catch(HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 061");
}
finally
{
sf.CloseConnection(conn);
}
return result;
}
public static bool executeNonStoredProcedure(string spname, IDictionary param)
{
bool result = false;
ISessionFactoryImplementor sf;
IDbCommand cmd;
IDbConnection conn;
try
{
Configuration cfg = new Configuration();
sf = (ISessionFactoryImplementor)cfg.BuildSessionFactory();
cmd = sf.ConnectionProvider.Driver.CreateCommand();
cmd.CommandText = spname;
cmd.CommandType = CommandType.StoredProcedure;
PopulateParameters(cmd, param);
conn = sf.OpenConnection();
}
catch(HibernateException ex)
{
Logger.Fatal(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "Fatal error occured, Can not connect the database!", "04 005");
}
try
{
cmd.Connection = conn;
cmd.ExecuteNonQuery();
result = true;
}
catch(HibernateException ex)
{
Logger.Error(typeof(DBHelper), ex.Message, ex);
throw new NHibernateException(ex, "04 062");
}
finally
{
sf.CloseConnection(conn);
}
return result;
}
#endregion
private static void PopulateParameters(System.Data.IDbCommand cmd, IDictionary param)
{
if(param == null || param.Count == 0)
return;
object[] keys = new object[param.Keys.Count];
param.Keys.CopyTo(keys, 0);
for (int i=0; i<keys.Length; i++)
{
IDbDataParameter parameter = cmd.CreateParameter();
parameter.ParameterName = (string)keys[i];
parameter.Value = param[keys[i]];
cmd.Parameters.Add(parameter);
}
}
}
}
4.HibernateUtil类,由于NHibernate和ADO.NET的不同设计思想,某些开发人员更喜欢于利用DataSet来绑定页面数据,但是NHibernate返回的所有结果都是IList,因此提供这个类中的方法将IList转换成DataSet用于数据绑定:
注意:实际使用时调用的就是最后的一系列public方法,前面的都是private方法
using System;
using System.Data;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using TPC.Hibernate.Entity;
namespace TPC.Hibernate.Core {
/// <summary>
/// HibernateUtil 的摘要说明。
/// </summary>
public class HibernateUtil {
# region Private Methods
private void addTableFromArray(IList list, Type[] types, DataSet wrapper, String tableName, string expression) {
////////////////////////////////////////////////////////////////////////////////////////
//Parse the expression and check whether the expression are coinside with the types
//
string[] columnNames = parseExpression(expression);
if(columnNames.Length != types.Length)
throw new Exception("Expression should include all columns of the list");
/////////////////
//Load Table
//
DataTable innerTable;
innerTable = wrapper.Tables.Add(tableName);
innerTable.BeginLoadData();
// Create title
createTitleRowByTypes(innerTable, types, columnNames);
if(list.Count > 0) {
if(list[0] is Array)
// Load data
loadTableFromArrayList(list, innerTable);
else
throw new Exception("The inner object of list must be Array");
}
innerTable.EndLoadData();
}
private void addTableFromEntity(IList list, Type type, DataSet wrapper, String tableName, string expression) {
/////////////////////////////////////////////
//Parse the expression if it is not * or null
//
string[] columnNames = null;
if((expression != "*") && (expression != string.Empty))
columnNames = parseExpression(expression);
//Get properties
PropertyInfo[] props = type.GetProperties();
/////////////////
//Load Table
//
DataTable innerTable;
innerTable = wrapper.Tables.Add(tableName);
innerTable.BeginLoadData();
// Create title
createTitleRowByEntityType(innerTable, props, columnNames);
if(list.Count > 0) {
if(list[0] is IEntity)
// Load data
loadTableFromEntity(list, innerTable, props, columnNames);
else
throw new Exception("The inner object of list must be IEntity");
}
innerTable.EndLoadData();
}
private string[] parseExpression(string expression) {
char[] token = {'|'};
string[] retVal = expression.Split(token);
return retVal;
}
private void createTitleRowByTypes(DataTable table, Type[] types, string[] columnNames) {
////////////////////////////////////////////////////////////////
//Asserts that the count of types equal that of the columnNames
//
int count = types.Length;
//To create columns
for(int i = 0; i < count; i ++) {
Type type = types[i];
string name = columnNames[i];
table.Columns.Add(name, type);
}
}
private void createTitleRowByEntityType(DataTable table, PropertyInfo[] props, string[] columns) {
//To create columns
//if columns is not null, to refer it, or else to ignore it
if(columns != null) {
//Convert string[] to ArrayList
ArrayList columnLst = new ArrayList(columns);
///////////////////////////////
//Add column refering columns[]
//
for(int i = 0; i < props.Length; i++) {
PropertyInfo prop = props[i];
//If the column was specified, to add it
if(columnLst.Contains(prop.Name))
table.Columns.Add(prop.Name, prop.PropertyType);
}
}
else {
//////////////////////////////////////
//Add column without refering columns[]
//
for(int i = 0; i < props.Length; i++) {
PropertyInfo prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
}
}
private void loadTableFromArrayList(IList list, DataTable table) {
// load data
foreach(object[] itm in list) {
table.LoadDataRow(itm, true);
}
}
private void loadTableFromEntity(IList list, DataTable table, PropertyInfo[] props, string[] columns) {
object[] vals = null;
int valIdx = 0;
if(columns != null) {
ArrayList columnLst = new ArrayList(columns);
vals = new object[columnLst.Count];
///////////////////////////////
//Load data from each entity
//
foreach(object obj in list) {
valIdx = 0;
for(int i = 0; i < props.Length; i++) {
PropertyInfo prop = props[i];
//If the column was specified, to add it
if(columnLst.Contains(prop.Name))
vals[valIdx++] = prop.GetValue(obj, null);
}
table.LoadDataRow(vals, true);
}
}
else {
vals = new object[props.Length];
//////////////////////////////////////
//Add column without refering columns[]
//
foreach(object obj in list) {
valIdx = 0;
for(int i = 0; i < props.Length; i++) {
PropertyInfo prop = props[i];
vals[valIdx++] = prop.GetValue(obj, null);
}
table.LoadDataRow(vals, true);
}
}
}
# endregion
# region Static Methods
/// <summary>
/// 将查询结果映射成DataSet
/// </summary>
/// <param name="list"></param>
/// <param name="type"></param>
/// <param name="expression">要映射的属性,"*"表示全部属性,可以用"Property1|Property2"表示
/// 属性的一个子集</param>
/// <returns></returns>
public static DataSet getDataSetFromArray(IList list, Type[] types, string expression) {
DataSet retVal = new DataSet();
HibernateUtil util = new HibernateUtil();
util.addTableFromArray(list, types, retVal, "", expression);
return retVal;
}
public static DataSet getDataSetFromEntity(IList list, Type type, string expression) {
DataSet retVal = new DataSet();
HibernateUtil util = new HibernateUtil();
util.addTableFromEntity(list, type, retVal, "", expression);
return retVal;
}
public static DataSet getDataSetFromArray(IList list, Type[] types, string tableName, string expression) {
DataSet retVal = new DataSet();
HibernateUtil util = new HibernateUtil();
util.addTableFromArray(list, types, retVal, tableName, expression);
return retVal;
}
public static DataSet getDataSetFromEntity(IList list, Type type, string tableName, string expression) {
DataSet retVal = new DataSet();
HibernateUtil util = new HibernateUtil();
util.addTableFromEntity(list, type, retVal, tableName, expression);
return retVal;
}
public static void getDataSetFromArray(IList list, Type[] types, DataSet dataSet, string expression) {
HibernateUtil util = new HibernateUtil();
util.addTableFromArray(list, types, dataSet, "", expression);
}
public static void getDataSetFromEntity(IList list, Type type, DataSet dataSet, string expression) {
HibernateUtil util = new HibernateUtil();
util.addTableFromEntity(list, type, dataSet, "", expression);
}
public static void getDataSetFromArray(IList list, Type[] types, DataSet dataSet, string tableName, string expression) {
HibernateUtil util = new HibernateUtil();
util.addTableFromArray(list, types, dataSet, tableName, expression);
}
public static void getDataSetFromEntity(IList list, Type type, DataSet dataSet, string tableName, string expression) {
HibernateUtil util = new HibernateUtil();
util.addTableFromEntity(list, type, dataSet, tableName, expression);
}
/// <summary>
/// 将查询结果映射成DataSet
/// </summary>
/// <param name="list"></param>
/// <param name="type"></param>
/// <param name="expression">要映射的属性,"*"表示全部属性,可以用"Property1|Property2"表示
/// 属性的一个子集</param>
/// <returns></returns>
public static bool AddDataSetFromIlist(ref DataSet Ds,IList list)
{
try
{
DataTable innerTable;
innerTable = Ds.Tables["table0"];
innerTable.BeginLoadData();
try
{
if(list.Count > 0)
{
if(list[0] is Array)
{
// Load data
object[] sublist;
System.Data.DataRow Dr;
for( int i = 0;i < list.Count;i++)
{
Dr = innerTable.NewRow();
sublist = (object[])list[i];
Dr.ItemArray = sublist;
innerTable.LoadDataRow(Dr.ItemArray,true);
}
}
else
{
throw new Exception("The inner object of list must be Array");
}
}
return true;
}
finally
{
innerTable.EndLoadData();
}
}
catch
{
return false;
}
}
/// <summary>
/// 从一个array数组中加入DataSet
/// </summary>
/// <param name="list"></param>
/// <param name="type"></param>
/// <param name="expression">要映射的属性,"*"表示全部属性,可以用"Property1|Property2"表示
/// 属性的一个子集</param>
/// <returns></returns>
public static bool AddDataSetFromArray(ref DataSet Ds,Array ary)
{
try
{
DataTable innerTable;
innerTable = Ds.Tables["table0"];
innerTable.BeginLoadData();
try
{
System.Data.DataRow Dr;
Dr = innerTable.NewRow();
Dr.ItemArray=(object[])ary;
innerTable.LoadDataRow(Dr.ItemArray,true);
return true;
}
finally
{
innerTable.EndLoadData();
}
}
catch
{
return false;
}
}
/// <summary>
/// 构造一个DataSet
/// </summary>
/// <param name="list"></param>
/// <param name="types"></param>
/// <param name="columnNames"></param>
/// <returns></returns>
public static DataSet CreateDataSet(Type[] types,string[] columnNames)
{
DataSet Ds = new DataSet();
if(columnNames.Length != types.Length)
throw new Exception("Expression should include all columns of the list");
/////////////////
//Load Table
//
DataTable innerTable;
innerTable = Ds.Tables.Add("table0");
innerTable.BeginLoadData();
// Create title
int count = types.Length;
//To create columns
for(int i = 0; i < count; i ++)
{
Type type = types[i];
string name = columnNames[i];
innerTable.Columns.Add(name, type);
}
innerTable.EndLoadData();
return Ds;
}
# endregion
}
}
七、程序中对DBHelper的调用
当执行查询操作时,没有事务,调用非常简单,DBHelper中包含很多个不同的查询方法:
string hql="from UserBE as user where user.Id=:userid order by user.UserId";
IDictionary param=new Hashtable();
param["userid"]=paramsSet["userid"];
IList result = DBHelper.getList(hql,param);
也可以不使用HQL语句,直接使用SQL,甚至执行存储过程执行查询和各种操作:(不推荐!)
public static IList executeSQL(string sql);
public static bool executeNonSQL(string sql);
public static IList executeStoredProcedure(string spname, IDictionary param);
public static bool executeNonStoredProcedure(string spname, IDictionary param);
例如:
string sql = "update TB_CmsUser set LastLogin = getdate() where UserId = '" + userId + "'";
return this.executeNonSQL(sql);
当执行Insert,Update,Delete时,如果没有事务,可以直接调用:
DBHelper.InsertObject(be);
DBHelper.UpdateObject(be);
DBHelper.deleteObject(be);
这三个方法在每次执行结束都都会自动提交,释放Session。
当执行包含事务的多个操作时,需要显式的开发事务和关闭事务:
ISession session=DBHelper.beginTransaction();
try
{
DBHelper.update(session,be);
string delhql="from UserRoleBE as userrole where userrole.UserId="+ be.ID;
DBHelper.delete(session,delhql,null);//delete userrole relationship
int count=rolelist.Count;
for(int i=0;i<count;i++)
{
UserRoleBE userrolebe=new UserRoleBE();
userrolebe.UserId=be.ID;
userrolebe.RoleId=int.Parse(rolelist[i].ToString());
DBHelper.insert(session,userrolebe);
}
}
catch(Exception ex)
{
throw ex;
}
finally
{
DBHelper.endTransaction(session);
}