一步步认识NHibernate的延迟加载

      说到NHibernate(以下简称NH),想必大家都非常熟悉了,他是.net中ORM的利器,算是非常强大,且使用的用户也非常多,那么一个强大的第三方框架,所包含的功能特性那肯定是很丰富的,其中就包括XML映射配置、延迟加载、HQL查询、原声SQL查询,什么二级缓存等等,总之入门是要有一定的时间和准备的,这里就把我所认识的“延迟”加载进行说明。

      什么是NH的延迟加载呢,延迟加载是Nh的一种机制,主要是解决不必要的查询对资源的浪费,只有当数据真正调用时才加载,而不会对数据进行提前加载造成资源的浪费,这句话是从网上看到的,其实就是这个意思,那么用我们程序的语言讲是这样的:就是有一个class,里面包含一个属性,属性的类型是一个集合(如IList<Role>),当我们需要加载一个对象(或者说记录吧)的时候,但又不想使用里面的那个集合属性,这个时候我们就可以使用延迟加载了,当一个类的集合使用了延迟加载的时候,那么只有使用这个集合的时候,NH才会发出SQL到数据库查询这个对象,这个就是传说中的延迟加载吧【个人理解】。

      上面说的“加载对象”和“使用集合属性”又是怎么理解呢?我们先说下Nh的加载对象的方法吧,总所周知,一个是Get,一个是Load,那么,他们又有什么区别呢,这个还是有点复杂的。

1. 对于get方法,NH会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null

2. load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论:

(1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException

(2)若为false,就跟get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException

以上转载http://blog.csdn.net/lenotang/article/details/2595349

我们再说一下“使用集合属性”,这个一般就两种情况,一种是通过调用session的Get或者Load方法加载对象,另外一种是在第一种的前提下,使用对象的一个属性,也就是点【.】一下。

下面就用代码开讲解“延迟加载”,“使用集合属性”。

实体类映射文件(根据类映射,也可以猜到具体的实体类是怎么样的,这里就不上传了):

View Code
 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="LBC.Model" assembly="LBC.Model">
 3   <class name="LBC.Model.User, LBC.Model" table="T_User" lazy="true">
 4     <id name="UserID" column="UserID">
 5       <generator class="assigned" />
 6     </id>
 7     <property name="UserName" column="UserName" />
 8     <property name="Password" column="Password" />
 9     <bag name="Roles" cascade="all" inverse="true" lazy="true" table="T_UserRole">
11       <key column="UserID"></key>
12       <many-to-many class="Role" column="RoleID" not-found="ignore"/>
13     </bag>
14   </class>
19 </hibernate-mapping>
View Code
 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="LBC.Model" assembly="LBC.Model">
 3   <class name="LBC.Model.Role, LBC.Model" table="T_Role" lazy="true">
 4     <id name="RoleID" column="RoleID">
 5       <generator class="identity" />
 6     </id>
 7     <property name="RoleName" column="RoleName" />
 8     <property name="Description" column="Description" />
 9     <property name="Expired" column="Expired" />
10     <bag name="Users" cascade="all" inverse="true" lazy="true" table="T_UserRole">
11       <key column="RoleID"></key>
12       <many-to-many class="User" not-found="ignore" column="UserID"/>
13     </bag>
14   </class>
15 </hibernate-mapping>

1、我们把User和Role这两个类级别的lazy修改为false,并使用session的Get方法加载一个User对象

这个时候,NH只生成了一条sql,说明没有去加载Roles集合【这样可以说明NH的Get对于集合属性是可以延迟加载的吧】

这里在加载User对象后去访问Roles集合属性,要把鼠标移动到roles变量那边,这里没有截图下来

很明显,这里NH发出了两条sql语句,一条是查询User对象,一条是查询Roles集合的

那么,把User和Role类级别的lazy修改为true又会是怎么样的呢,结果是跟false一样,这里就不做说明了

2、我们把User和Role这两个类级别的lazy修改为false,且User映射文件的bag里面的lazy也修改为false,并使用session的Get方法加载一个User对象

这个时候,NH生成了两条sql语句,跟上面的情况(加载后去访问集合属性)是一样的。那么我们再把类级别的lazy修改为false会是什么结果呢,当然,结果还是一样的,那么通过以上的描述,我们可以知道,NH在使用session的Get加载对象的时候,也是可以延迟加载的,至少是我们在没有去访问集合属性的时候NH是不会生成sql的,这个应该就是我们说的延迟的道理【这里说的延迟仅对于集合属性,而非类本身】。

3、我们把User和Role这两个类级别的lazy修改为false,且User映射文件的bag里面的lazy修改为true,并使用session的Load方法加载一个User对象

 我们可以看到,使用Load加载,且类级别lazy为false,bag的lazy为true的时候,NH只生成一条sql

4、我们把User和Role这两个类级别的lazy修改为true,且User映射文件的bag里面的lazy修改为true,并使用session的Load方法加载一个User对象

喔,NH竟然一条sql都没有生成,真实大快人心啊。当我们把鼠标移动到user变量的时候,NH就会生成加载User对象的sql,如果再访问user的Roles属性,那么又会生成一条加载roles的sql。

 

最后,我们来个大总结吧:

1、NH的延迟加载的意思就是:你访问了属性(就是点一个属性出来,除了ID之外)就生成sql到数据库加载

2、NH的在延迟加载有2个方面,一个是延迟加载类的;另外一个是延迟加载集合的

3、NH的类延迟加载仅对于使用session的Load方法时才有效【如果使用Get的话,不管类级别的lazy怎么配置,始终会生成一条加载类本身的sql】

4、NH的集合延迟加载,对于session的Get和Load方法都是有效的

5、如果把类级别的lazy设置为false,那么不管是Get还是Load都发生成一条加载类本身的sql

6、如果把类级别的lazy设置为true,那么Get会生成加载类本身的sql,而Load不会

posted @ 2011-09-12 12:14  lawbc  阅读(3114)  评论(8编辑  收藏  举报