NHibernate的关键点精要

本文围绕NHibernate展开,作者学习时遇到诸多疑惑,后通过书籍解惑。介绍了持久层开发路线,探讨Lazy Load与清晰DAL Service层的矛盾,还讲解了inverse属性、类表映射、继承关系映射方法,以及告知hbm2DDL生成ntext属性的设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        学习NHibernate,使用NHibernate总是有太多的疑惑。原来以后不就是个配置文件,后来才发现远远没有那么简单,这个配置文件又要照顾Entity Class,又要照顾数据库脚本生成,更要照顾动态的SQL语句生成,所以现在想来碰到这么多问题也是正常了。这些问题,有些在同事间、或者自己琢磨已经有结果,但是有很多问题是直到看了孙姐姐的书《精通HIBERNATE:Java对象持久化技术详解》,才豁然开朗。NHibernate组织自己也说,现在离1.0的最终发布版本差的就是文档了,所以NHibernate文档的缺乏更加加深了掌握的难度,但是幸好它是Hibernate的一个port,所以学习NHibernate最好的资料就是Hibernate的各种介绍和书籍了。下面就讲我认为比较重要的一一列出了。

1、持久层开发的最佳路线图:hbm->entity class->database scripts
hbm是信息最丰富的,所以由它作为驱动因素是最理想的,当然如果是项目的约束的影响那就另当别论了(比如数据库已经设计好了)。从hbm->entity class可以使用NHibernateContrib库(NHibernate官方开发的),最后生成database scripts的功能NHibernate库已经自带了。写hbm实际上不困难,只有将NHibernate自带的几个xsd文件拷贝到VS相应目录下就可以使用智能提醒了,写起来可谓是运指如飞。另外有个叫Object Mapper的工具提供了从UML图自动生成hbm的功能,不过我试了一下不是很爽,没有直接写快。

2、要Lazy Load,还是要清晰的DAL Service层?
(N)Hibernate一再强调Lazy Load的重要性,所他如何可以增加程序的性能,但是经过一段时间的研究发现Lazy Load固然好,但是可能会破坏企业应用架构。首先Lazy Load的使用有一个前提就是必须在同一个Session中,否则NHibernate给Lazy Load的类加的动态代理的事前拦截将无法捕获合适的Session因此在后来读Lazy Load的属性时会去用已经关闭的Session,自然就会throw exception了。另外,按照依赖颠倒的原则,一般我们都这样来BLL层下的架构“BLL ----> IDAL <--- DALImpl”,那么在DAL的每个方法中,必须经历Session打开和关闭的过程,因此当BLL层获得某个持久类,实际上是游离状态的,即Session已经关闭,此时BLL层在去读设置了Lazy Load的属性,就注定失败了。那么如何才能应用Lazy Load?一个办法在BLL中用NHiberante的Session等,但这样显然破坏了原本清晰的架构。所以我的理解是:Lazy Load和清晰的DAL Service层,二者不可兼得。当然最好是我理解错了,:)

3、inverse属性
该属性通常存在于双向管理关系中的<set>等列表标签中,其含义是“是否是镜像”的含义。比如在many-to-one双向关联关系中One方的<set>标签中若设置了inverse=true,则表示One方的关系设定只是一个镜像,而该关系的最终生成SQL则完全用Many方决定。因此当运行一下脚本:







1 None.gif One theOne  =   new  One();
2 None.gifMany theMany  =  one Many();
3 None.gif
4 None.gif //  此处省略常规属性的赋值
5 None.gif
6 None.giftheOne.TheMany  =  theMany;
7 None.giftheMany.OneList.Add(theOne);
数据库只会生成一条插入到TableMany中INSERT语句。
如果将One方<set>标签中的inverse属性改成false,则就会生成2条SQL语句,一条是插入到TableMany表中,第二条是UPDATE TableMany表中的外键使得其指向TableOne中的theOne记录。其实道理也很简单,如果“是否是镜像”为否,则由One方自己控制,它能控制的方法就是UPDATE TableMany表了,所以就2条SQL语句了。由此也可以推出若One方的<set>标签中若设置了inverse=true,代码片断中的第7行也可以不需要了。

4、实体类间组合关系
组合关系在UML中用实心的菱形表示,意思是同生共死。那么在NHibernate中就应该在父方加上cascade="all-delete-orphan"如下代码所示:
1 None.gif < set 
2 None.gif     name ="orders"  
3 None.gif    cascade ="all-delete-orphan"  
4 None.gif    inverse =true>
5 None.gif     <key column ="CustomerId"   />
6 None.gif     < one-to-many  class ="Order"   />
7 None.gif </ set >

这样设置后NHibernate会帮助完成以下3件事情:
1、级联保存或更新,相当于cascade属性设置了"save-update"的情况。
2、级联删除,相当于cascade属性设置了"delete"的情况。
3、删除没有父的所有子对象。

5、类图中是2个类,但存储使用一个表
这种情况很多,比如一个系统中有用户和地址,在类图中应该是2个类会比较清晰,但是有时确希望在数据库中存放在1个表中,这时候可以用<component />标记,如下所示:






 1 None.gif < class  name ="User" >
 2 None.gif     < id  name ="Id" >
 3 None.gif         < generator  class ="native" />
 4 None.gif     </ id >
 5 None.gif     < property  name ="FirstName" />
 6 None.gif     < property  name ="LastName" />
 7 None.gif     < component  name ="Component"  class ="UserSettings" >
 8 None.gif         < property  name ="Address1" />
 9 None.gif         < property  name ="Address2" />
10 None.gif         < property  name ="Street" />
11 None.gif         < property  name ="State" />
12 None.gif         < property  name ="Country" />
13 None.gif     </ component >
14 None.gif </ class >


6、继承关系的几种映射方法

继承关系的映射可以有3种方法:每个具体类映射一张表、全部映射成一张表、每个类映射一张表(包括抽象类),下面是实例,有一个基类:Animal,二个子类:Dog、Cat。

每个具体类映射一张表:

 1 None.gif < class  name ="Dog" >
 2 None.gif     < id  name ="Id" >
 3 None.gif         < generator  class ="native" />
 4 None.gif     </ id >
 5 None.gif     < property  name ="Name" />
 6 None.gif </ class >
 7 None.gif
 8 None.gif < class  name ="Cat" >
 9 None.gif     < id  name ="Id" >
10 None.gif         < generator  class ="native" />
11 None.gif     </ id >
12 None.gif     < property  name ="Name" />
13 None.gif </ class >

这种方式实际上是没有继承,不能使用以下查询语句:

1 None.gif List Animals  =  session.find( " from Animal " );


全部映射一张表:

 1 None.gif < class  name ="Animal"   >
 2 None.gif     < id  name ="Id" >
 3 None.gif         < generator  class ="native" />
 4 None.gif     </ id >
 5 None.gif     < discriminator  column ="Type"  type ="string"   />
 6 None.gif     < property  name ="Name" />
 7 None.gif
 8 None.gif     < subclass  name ="Dog"  discriminator-value ="D" >
 9 None.gif     </ subclass >
10 None.gif
11 None.gif     < subclass  name ="Cat"  discriminator-value ="C" >
12 None.gif     </ subclass >
13 None.gif
14 None.gif </ class >


每个类一个表(包括抽象类):

 1 None.gif < class  name ="Animal"   >
 2 None.gif     < id  name ="Id" >
 3 None.gif         < generator  class ="native" />
 4 None.gif     </ id >
 5 None.gif     < discriminator  column ="Type"  type ="string"   />
 6 None.gif     < property  name ="Name" />
 7 None.gif
 8 None.gif     < joined-subclass  name ="Dog" >
 9 None.gif         < key  column ="DogId"   />
10 None.gif     </ joined-subclass >
11 None.gif
12 None.gif     < joined-subclass  name ="Cat" >
13 None.gif         < key  column ="CatId"   />
14 None.gif     </ joined-subclass >
15 None.gif </ class >


7、如何告知hbm2DDL,我要生成ntext属性?
type="StringClob"是不管用得,应该是type="String" 然后设置一个大于4000的值给length,如length=5000,即可。

215686.html

浅水滩 2005-08-16 00:07 发表评论





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值