一步步认识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方法加载对象,另外一种是在第一种的前提下,使用对象的一个属性,也就是点【.】一下。
下面就用代码开讲解“延迟加载”,“使用集合属性”。
实体类映射文件(根据类映射,也可以猜到具体的实体类是怎么样的,这里就不上传了):
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>
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不会