摘要
NHibernate的延迟加载机制是很重要的内容。通过关系映射将数据库表之间的关系映射成对象之间的关系,如果没有延迟加载机制,从主表的一个对象的查询将直接查询出所有与该对象关联的其他对象,如果关联的对象上还有其他的关联对象,还要去查询其他的对象。如果这张“网”非常大,或者关联的数据库记录非常多的话,每次查询主表记录都要把整个数据库都查询一遍,这样效率会非常低下。为了解决这个问题产生了NHibernate延迟加载。对一些属性设置延迟加载,只在对象访问到这些属性的时候才去查询数据库,很大程度上提高了系统性能。
属性是否使用延迟加载在映射文件里设置lazy="true|false"来打开|关闭延迟加载。(NHibnerate默认是开启延迟加载,默认值是true,使用延迟加载)。
对于关联映射的属性,如果查询了主表数据,查询后程序中没有去读其关联属性,这时候如果将Session关闭,NHibernate就不能为我们实现延迟加载。换句话说,对于关联属性,延迟加载只在Session关闭之前有效。
程序演示
1、默认使用延迟加载的情况
假设数据库Customer表里有一条Id为2的记录,与之关联的Order表里有两条Order记录。
var customer = customerService.GetById(2);
执行此行代码得到的监控结果。
监控结果显示,只查询了Customer表记录。
如果将程序改成下面这样:
1 var customer = customerService.GetById(2); 2 var orders = customer.Orders; 3 int orderCount = orders.Count;
再次运行程序,得到监控结果:
在执行这句下面赋值语句的时候,才去执行Order表的查询。
int orderCount = orders.Count;
2、关闭延迟加载
修改Customer.hbm.xml文件。
<set name="Orders" table="`Order`" cascade="all-delete-orphan" inverse="true" lazy="false"> <key column="CustomerId"/> <one-to-many class="Order"/> </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(2); 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对象的时候产生了异常。
如果删除这行代码程序能够正常运行。
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(2); 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文件。
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Demo.XML.Entities" namespace="Demo.XML.Entities.Domain"> <class name="Customer" table="Customer"> <id name="Id"> <generator class="native"/> </id> <property name="FirstName" not-null="true"/> <property name="LastName" not-null ="true"/> <property name="AverageRating"/> <property name="Points"/> <property name="HasGoldStatus"/> <property name="MemberSince"/> <property name="CreditRating" type="CustomerCreditRating"/> <component name="Address"> <property name="Street"/> <property name="City"/> <property name="Province"/> <property name="Country"/> </component> <set name="Orders" table="`Order`" cascade="all-delete-orphan" inverse="true" fetch="join"> <key column="CustomerId"/> <one-to-many class="Order"/> </set> </class> </hibernate-mapping>
修改Main函数。
1 static void Main(string[] args) 2 { 3 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize(); 4 5 var customer = customerService.GetById(2); 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"强制关闭某一个属性的延迟加载。