摘要

NHibernate的延迟加载机制是很重要的内容。通过关系映射将数据库表之间的关系映射成对象之间的关系,如果没有延迟加载机制,从主表的一个对象的查询将直接查询出所有与该对象关联的其他对象,如果关联的对象上还有其他的关联对象,还要去查询其他的对象。如果这张“网”非常大,或者关联的数据库记录非常多的话,每次查询主表记录都要把整个数据库都查询一遍,这样效率会非常低下。为了解决这个问题产生了NHibernate延迟加载。对一些属性设置延迟加载,只在对象访问到这些属性的时候才去查询数据库,很大程度上提高了系统性能。

属性是否使用延迟加载在映射文件里设置lazy="true|false"来打开|关闭延迟加载。(NHibnerate默认是开启延迟加载,默认值是true,使用延迟加载)。

对于关联映射的属性,如果查询了主表数据,查询后程序中没有去读其关联属性,这时候如果将Session关闭,NHibernate就不能为我们实现延迟加载。换句话说,对于关联属性,延迟加载只在Session关闭之前有效。

程序演示

1、默认使用延迟加载的情况

假设数据库Customer表里有一条Id为2的记录,与之关联的Order表里有两条Order记录。

  1. var customer = customerService.GetById();

执行此行代码得到的监控结果。

监控结果显示,只查询了Customer表记录。

如果将程序改成下面这样:

  1. var customer = customerService.GetById();
  2. var orders = customer.Orders;
  3. int orderCount = orders.Count;

再次运行程序,得到监控结果:

在执行这句下面赋值语句的时候,才去执行Order表的查询。

  1. int orderCount = orders.Count;

2、关闭延迟加载

修改Customer.hbm.xml文件。

  1. <set name="Orders" table="`Order`" cascade="all-delete-orphan" inverse="true" lazy="false">
  2. <key column="CustomerId"/>
  3. <one-to-many class="Order"/>
  4. </set>

执行同样的查询语句,得到监控结果。

监控记录显示,在查询Customer的同时,还查询了Order记录。

只有在关联的属性记录数量不是很多的情况下才使用lazy="false",关掉延迟加载。

 3、延迟加载和关闭Session

上面提到对于关联属性,延迟加载只在Session关闭之前有效。这里用程序演示一下。

修改Main函数。

  1. using Demo.Service;
  2. using Demo.Service.Interface;
  3. using System;
  4.  
  5. namespace Demo.ConsoleApp
  6. {
  7. class Program
  8. {
  9. static readonly ICustomerService customerService = new CustomerService();
  10. static readonly IOrderService orderService = new OrderService();
  11. static readonly IProductService productService = new ProductService();
  12.  
  13. static void Main(string[] args)
  14. {
  15. HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
  16.  
  17. var customer = customerService.GetById();
  18. Service.Infrastructure.SessionManager.CloseSession();
  19. var orders = customer.Orders;
  20. foreach (var order in orders)
  21. {
  22. Console.WriteLine(order.Id);
  23. }
  24.  
  25. Console.WriteLine("Completed");
  26. Console.ReadLine();
  27. }
  28. }
  29. }

执行程序,得到监控结果。

程序在执行for-each循环取Order对象的时候产生了异常。

如果删除这行代码程序能够正常运行。

  1. Service.Infrastructure.SessionManager.CloseSession();

因此,为了能够利用对象的延迟加载,我们通常的做法是在查询完成后,不关闭Session。

有两种方法可以在关闭Session前,强制立即加载对象关联属性。

1)调用NHibernateUtil.Initialize静态方法,在Session关闭之前强制对指定的对象属性立即加载。

修改Main函数。

  1. static void Main(string[] args)
  2. {
  3. HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
  4.  
  5. var customer = customerService.GetById();
  6. NHibernate.NHibernateUtil.Initialize(customer.Orders);
  7. Service.Infrastructure.SessionManager.CloseSession();
  8.  
  9. var orders = customer.Orders;
  10. foreach (var order in orders)
  11. {
  12. Console.WriteLine(order.Id);
  13. }
  14.  
  15. Console.WriteLine("Completed");
  16. Console.ReadLine();
  17. }

执行程序,得到监控结果。

2)在关联映射配置节中定义fetch="join",指出对于关联属性立即加载。

修改Customer.hbm.xml文件。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Demo.XML.Entities" namespace="Demo.XML.Entities.Domain">
  3. <class name="Customer" table="Customer">
  4. <id name="Id">
  5. <generator class="native"/>
  6. </id>
  7. <property name="FirstName" not-null="true"/>
  8. <property name="LastName" not-null ="true"/>
  9. <property name="AverageRating"/>
  10. <property name="Points"/>
  11. <property name="HasGoldStatus"/>
  12. <property name="MemberSince"/>
  13. <property name="CreditRating" type="CustomerCreditRating"/>
  14. <component name="Address">
  15. <property name="Street"/>
  16. <property name="City"/>
  17. <property name="Province"/>
  18. <property name="Country"/>
  19. </component>
  20. <set name="Orders" table="`Order`" cascade="all-delete-orphan" inverse="true" fetch="join">
  21. <key column="CustomerId"/>
  22. <one-to-many class="Order"/>
  23. </set>
  24. </class>
  25. </hibernate-mapping>

修改Main函数。

  1. static void Main(string[] args)
  2. {
  3. HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
  4.  
  5. var customer = customerService.GetById();
  6. Service.Infrastructure.SessionManager.CloseSession();
  7.  
  8. var orders = customer.Orders;
  9. foreach (var order in orders)
  10. {
  11. Console.WriteLine(order.Id);
  12. }
  13.  
  14. Console.WriteLine("Completed");
  15. Console.ReadLine();
  16. }

执行程序,得到监控结果。

从监控结果看到,这次的查询SQL语句跟以前都不一样。之前执行了两条SQL语句,分别查询Customer表和Order表。这次只执行了一次SQL查询,从主表Customer到从表Order的left join查询。

有兴趣的可以用大数据测试的方法,比较哪种方法执行效率要高。

结语

NHibernate的延迟加载是NHibernate很重要的一个性能优化,NHibernate默认是开启延迟加载的,可以通过设置属性的lazy="false"强制关闭某一个属性的延迟加载。

NHibernate系列文章二十一:延迟加载的更多相关文章

  1. NHibernate系列文章二十四:NHibernate查询之Linq查询(附程序下载)

    摘要 NHibernate从3.0开始支持Linq查询.写Linq to NHibernate查询就跟写.net linq代码一样,非常灵活,可以很容易实现复杂的查询.这篇文章使用Linq to NH ...

  2. NHibernate系列文章二十八:NHibernate Mapping之Auto Mapping(附程序下载)

    摘要 上一篇文章介绍了Fluent NHibernate基础知识.但是,Fluent NHibernate提供了一种更方便的Mapping方法称为Auto Mapping.只需在代码中定义一些Conv ...

  3. NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)

    摘要 从这一节起,介绍NHibernate Mapping的内容.前面文章都是使用的NHibernate XML Mapping.NHibernate XML Mapping是NHibernate最早 ...

  4. NHibernate系列文章二十五:NHibernate查询之Query Over查询(附程序下载)

    摘要 这一篇文章介绍在NHibernate 3.2里引入的Query Over查询,Query Over查询跟Criteria查询类似.首先创建IQueryOver对象,然后通过调用该对象的API函数 ...

  5. NHibernate系列文章二十三:NHibernate查询之Criteria查询(附程序下载)

    摘要 上一篇文章介绍了NHibernate HQL,他的缺点是不能够在编译时发现问题.如果数据库表结构有改动引起了实体关系映射的类有改动,要同时修改这些HQL字符串.这篇文章介绍NHibernate面 ...

  6. NHibernate系列文章二十二:NHibernate查询之HQL查询(附程序下载)

    摘要 NHibernate提供了多种查询方式,最早的HQL语言查询.Criteria查询和SQL Query,到NHibernate 3.0的Linq NHibernate,NHIbernate 4. ...

  7. NHibernate系列文章二十:NHibernate关系之一对一(附程序下载)

    摘要 NHibernate一对一关系虽然不经常碰到,但是在对于数据库结构优化的时候,经常会碰到一对一关系.比如,产品详细信息比较多的时候,可以把产品详细信息放到另一张表里面,Product主表只记录产 ...

  8. NHibernate系列文章二:创建NHibernate工程

    摘要 这篇文章介绍了如何创建一个简单的使用NHibernate的控制台应用程序,包括使用NuGet.简单的配置.单表映射.对NHibernate配置文件添加智能提示.使用ISessionFactory ...

  9. NHibernate系列文章二十六:NHibernate查询之SQL Query查询(附程序下载)

    摘要 NHibernate在很早的版本就提供了SQL Query(原生SQL查询),对于很复杂的查询,如果使用其他的查询方式实现比较困难的时候,一般使用SQL Query.使用SQL Query是基于 ...

随机推荐

  1. toggle()方法和hove()方法

    toggle()语法结构: toggle(fn1,fn2,fn3,....fnN); 第一次单击元素,触发第一个元素,再次单击触发第二个元素,如果有更多元素,依次触发,直到最后一个元素,随后单击反复对 ...

  2. updatepanel 回发或回调参数无效

    不同于网上的其它情况,这个是由于通过js修改了服务器控件Select的列表项数目,导致验证viewstate时出现的问题.最后改为通过服务器代码来给Select加选项,就不会报这个错误了. 服务器控件 ...

  3. 中转Http请求

    应用场景:公司与外部公司数据对接,外部公司需申请指定IP访问.而本地ip经常变动,无法因ip变动时刻向外部公司申请绑定IP,给本地程序调试带来麻烦,故只能在指定ip服务器上搭建请求中转http请求: ...

  4. JS 深度拷贝 Object Array

    JS 深度拷贝 Object Array function cloneObj(o) { var isArray = o instanceof Array; var isObject = o insta ...

  5. Redis执行Lua脚本的情况

    第一个测试: 往Redis里面存入1000个Hash,每个Hash里面有100个元素(Key 0-99,值是Key^2). PHP代码,执行33s左右 <?php $redis = new Re ...

  6. Hibernate5.2之一对一外键关联(五)

                                                     Hibernate5.2之一对一外键关联(五) 一.简介 上篇文章中笔者介绍了Hibernate关联关 ...

  7. Easyui 小脚本

    function addTab(subtitle, url, icon) { if (!$('#tabs').tabs('exists', subtitle)) { $('#tabs').tabs(' ...

  8. 关于Memcached一致性hash的探究

    参考文章 http://blog.chinaunix.net/uid-20498361-id-4303232.html http://blog.csdn.net/kongqz/article/deta ...

  9. things to analysis

    Started by timer [EnvInject] - Loading node environment variables. Building remotely on RE_DEV_BUILD ...

  10. XE6 & IOS开发之开发者账号、苹果证书(2):关于苹果证书

    网上能找到的关于Delphi XE系列的移动开发的相关文章甚少,本文尽量以详细的图文内容.傻瓜式的表达来告诉你想要的答案. 原创作品,请尊重作者劳动成果,转载请注明出处!!! 1.关于苹果证书. 注意 ...