[转]NHibernate之旅(12):初探延迟加载机制

本节内容

  • 引入
  • 延迟加载
  • 实例分析
    • 1.一对多关系实例
    • 2.多对多关系实例
  • 结语

引入

通过前面文章的分析,我们知道了如何使用NHibernate,比如CRUD操作、事务、一对多、多对多映射等问题,这篇我们初步探索NHibernate中的加载机制。

在讨论之前,我们看看我们使用的数据模型,回顾一下第二篇建立的数据模型。

Customer与Orders是一对多关系,Order与Product是多对多关系。这一篇还是使用这个模型,有关具体配置和映射参考本系列的文章。

延迟加载(Lazy Loading)

延迟加载按我现在的理解应该叫“视需要加载(load-on-demand)”,“(delayed loading)”,“刚好及时加载(just-in-time loading)”在合适不过了。这里按字面理解延迟仿佛变成了“延迟,延长,拖延时间”的意思。

NHibernate从1.2版本就默认支持了延迟加载。其实延迟加载的执行方式是使用GoF23中的代理模式,我们用一张图片来大致展示延迟加载机制。

实例分析

1.一对多关系实例

在一对多关系实例中,我们使用Customer对象与Order对象为例,在数据访问层中编写两个方法用于在测试时调用,分别是:

数据访问层中方法一:加载Customer对象

public Customer LazyLoad(int customerId)
{
    return _session.Get<Customer>(customerId);
}

数据访问层中方法二:加载Customer对象并使用Using强制清理关闭Session

public Customer LazyLoadUsingSession(int customerId)
{
    using (ISession _session = new SessionManager().GetSession())
    {
        return _session.Get<Customer>(customerId);
    }
}

1.默认延迟加载

调用数据访问层中的LazyLoad方法加载一个Customer对象,NHibernate的默认延迟加载Customer关联的Order对象。利用NHibernate提供有用类(NHibernateUtil)测试被关联的Customer对象集合是否已初始化(也就是已加载)。

[Test]
public void LazyLoadTest()
{
    Customer customer = _relation.LazyLoad(1);
    Assert.IsFalse(NHibernateUtil.IsInitialized(customer.Orders));
}

测试成功,观察NHibernate生成SQL语句为一条查询Customer对象的语句。我们使用调试发现,Orders对象集合的属性值为:{Iesi.Collections.Generic.HashedSet`1[DomainModel.Entities.Order]},并可以同时看到Order对象集合中的项。截图如下:

2.延迟加载并关闭Session

同第一个测试相同,这个测试调用使用Using强制资源清理Session加载Customer对象的方法。

[Test]
public void LazyLoadUsingSessionTest()
{
    Customer customer = _relation.LazyLoadUsingSession(1);
    Assert.IsFalse(NHibernateUtil.IsInitialized(customer.Orders));
}

测试成功,其生成SQL语句和上面测试生成SQL语句相同。但是使用调试发现,Orders对象集合的属性值为:NHibernate.Collection.Generic.PersistentGenericSet<DomainModel.Entities.Order> ,如果你进一步想看看Order对象集合中的项,它抛出了HibernateException异常:failed to lazily initialize a collection, no session or session was closed。截图如下:

2.多对多关系实例

同理,在多对多关系实例中,我们以Order对象与Products对象为例,我们在数据访问层中写两个方法用于测试:

方法1:加载Order对象

public DomainModel.Entities.Order LazyLoadOrderAggregate(int orderId)
{
    return _session.Get<DomainModel.Entities.Order>(orderId);
}

方法2:加载Customer对象并使用Using强制清理关闭Session

public DomainModel.Entities.Order LazyLoadOrderAggregateUsingSession(int orderId)
{
    using (ISession _session = new SessionManager().GetSession())
    {
        return _session.Get<DomainModel.Entities.Order>(orderId);
    }
}

1.默认延迟加载

调用数据访问层中的LazyLoadOrderAggregate方法加载一个Order对象,NHibernate的默认延迟加载Order关联的Products对象集合(多对多关系),利用代理模式加载Customer对象集合(多对一关系)。利用NHibernate提供有用类(NHibernateUtil)测试被关联的Products对象集合和Customer对象集合是否已初始化。

[Test]
public void LazyLoadOrderAggregateTest()
{
    Order order = _relation.LazyLoadOrderAggregate(2);
    Assert.IsFalse(NHibernateUtil.IsInitialized(order.Customer));
    Assert.IsFalse(NHibernateUtil.IsInitialized(order.Products));
}

测试成功,NHibernate生成SQL语句如下:

SELECT order0_.OrderId as OrderId1_0_,
       order0_.Version as Version1_0_,
       order0_.OrderDate as OrderDate1_0_,
       order0_.Customer as Customer1_0_
FROM [Order] order0_ WHERE [email protected]; @p0 = ‘2‘

调试看看效果截图,可以清楚的观察到Customer对象和Products对象集合的类型。

2.延迟加载并关闭Session

同第一个测试相同,这个测试调用使用Using强制资源清理Session加载Order对象的方法。

[Test]
public void LazyLoadOrderAggregateUsingSessionTest()
{
    Order order = _relation.LazyLoadOrderAggregateUsingSession(2);
    Assert.IsFalse(NHibernateUtil.IsInitialized(order.Customer));
    Assert.IsFalse(NHibernateUtil.IsInitialized(order.Products));
}

测试成功,其生成SQL语句和上面测试生成SQL语句相同。但是使用调试发现,Customer对象类型为:{CustomerProxy9dfb54eca50247f69bfedd92e1638ba5},进一步观察Customer对象Firstname、Lastname等项引发了“NHibernate.LazyInitializationException”类型的异常。Products对象集合的属性值为:{NHibernate.Collection.Generic.PersistentGenericBag<DomainModel.Entities.Product>},如果你进一步想看看Products对象集合中的项它同样抛出HibernateException异常:failed to lazily initialize a collection, no session or session was closed。下面截取获取Customer对象一部分图,你想知道全部自己亲自调试一把:

3.延迟加载中LazyInitializationException异常

上面测试已经说明了这个问题:如果我想在Session清理关闭之后访问Order对象中的某些项会得到一个异常,由于session关闭,NHibernate不能为我们延迟加载Order项,我们编写一个测试方法验证一下:

[Test]
[ExpectedException(typeof(LazyInitializationException))]
public void LazyLoadOrderAggregateUsingSessionOnFailTest()
{
    Order order = _relation.LazyLoadOrderAggregateUsingSession(2);
    string name = order.Customer.Name.Fullname;
}

上面的测试抛出“Could not initialize proxy - no Session”预计的LazyInitializationException异常,表明测试成功,证明不能加载Order对象的Customer对象。

4.N+1选择问题

我们在加载Order后访问Product项,导致访问Product每项就会产生一个选择语句,我们用一个测试方法来模拟这种情况:

[Test]
public void LazyLoadOrderAggregateSelectBehaviorTest()
{
    Order order = _relation.LazyLoadOrderAggregate(2);
    float sum = 0.0F;
    foreach (var item in order.Products)
    {
        sum += item.Cost;
    }
    Assert.AreEqual(21.0F, sum);
}

NHibernate生成SQL语句如下:

SELECT order0_.OrderId as OrderId1_0_,
       order0_.Version as Version1_0_,
       order0_.OrderDate as OrderDate1_0_,
       order0_.Customer as Customer1_0_
FROM [Order] order0_ WHERE [email protected]; @p0 = ‘2‘

SELECT products0_.[Order] as Order1_1_,
       products0_.Product as Product1_,
       product1_.ProductId as ProductId3_0_,
       product1_.Version as Version3_0_,
       product1_.Name as Name3_0_,
       product1_.Cost as Cost3_0_
FROM OrderProduct products0_
left outer join Product product1_ on
products0_.Product=product1_.ProductId
WHERE products0_.[Order][email protected]; @p0 = ‘2‘

这次我走运了,NHibernate自动生成最优化的查询语句,一口气加载了两个Product对象。但是试想一下有一个集合对象有100项,而你仅仅需要访问其中的一两项。这样加载所有项显然是资源的浪费。

幸好,NHibernate为这些问题有一个方案,它就是立即加载。欲知事后如何,请听下回分解!

时间: 2024-10-07 09:47:35

[转]NHibernate之旅(12):初探延迟加载机制的相关文章

NHibernate之旅系列文章导航

NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实例.NHibernate2.0.NHibernate2.0教程.NHibernate之旅.NHibernate工具 我的NHibernate全部文章在http://www.cnblogs.com/lyj/category/129155.html 导游 NHibernate是把Java的Hiberna

NHibernate之旅(13):初探立即加载机制

本节内容 引入 立即加载 实例分析 1.一对多关系实例 2.多对多关系实例 结语 引入 通过上一篇的介绍,我们知道了NHibernate中默认的加载机制--延迟加载.其本质就是使用GoF23中代理模式实现,这节我们简单分析NHibernate另一种加载机制--立即加载.我用一张图片形象的展现立即加载机制. 立即加载 顾名思义,就是立刻加载相关联对象集合,与延迟加载相反.我们可以使用三种方法来立即加载,分别是:可选的lazy属性.NHibernate提供的实用类.HQL抓取策略.下面依次用实例分析

[转]NHibernate之旅(13):初探立即加载机制

本节内容 引入 立即加载 实例分析 1.一对多关系实例 2.多对多关系实例 结语 引入 通过上一篇的介绍,我们知道了NHibernate中默认的加载机制——延迟加载.其本质就是使用GoF23中代理模式实现,这节我们简单分析NHibernate另一种加载机制——立即加载.我用一张图片形象的展现立即加载机制. 立即加载 顾名思义,就是立刻加载相关联对象集合,与延迟加载相反.我们可以使用三种方法来立即加载,分别是:可选的lazy属性.NHibernate提供的实用类.HQL抓取策略.下面依次用实例分析

NHibernate之旅(18):初探代码生成工具使用

本节内容 引入 代码生成工具 结语 引入 我们花了大量的篇幅介绍了相关NHibernate的知识.一直都是带着大家手动编写代码,首先创建数据库架构.然后编写持久化类和映射文件,最后编写数据操作方法.測试方法. 这是典型的数据库驱动开发(DbDD,Database-Driven Developent)技术.可是自己不是这样做的,我先编写持久化类和映射文件,然后偷偷的使用SchemaExport工具把数据库生成了.按上面的步骤写文章的,关于SchemaExport工具就是下一篇的事情了,这篇说说利用

【SQL进阶】03.执行计划之旅1 - 初探

听到大牛们说执行计划,总是很惶恐,是对知识的缺乏的惶恐,所以必须得学习执行计划,以减少对这一块知识的惶恐,下面是对执行计划的第一讲-理解执行计划. 本系列[T-SQL]主要是针对T-SQL的总结. SQL基础 [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL

NHibernate之旅(2):第一个NHibernate程序

本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4-1.设计持久化类 4-2.编写映射文件 5.数据访问层 5-1.辅助类 5-2.编写操作 6.数据访问层的测试 6-1.配置NHibernate 6-2.测试 结语 作者注:2009-11-06已更新 开始使用NHibernate 我们亲自动手,一步一步搭建一个NHibernate程序来,我以一个实际场景电子交易程序来模拟,客户/订单/产品的经典组合.由于是第一

[转]NHibernate之旅(2):第一个NHibernate程序

本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4-1.设计持久化类 4-2.编写映射文件 5.数据访问层 5-1.辅助类 5-2.编写操作 6.数据访问层的测试 6-1.配置NHibernate 6-2.测试 结语 作者注:2009-11-06已更新 开始使用NHibernate 我们亲自动手,一步一步搭建一个NHibernate程序来,我以一个实际场景电子交易程序来模拟,客户/订单/产品的经典组合.由于是第一

Hibernate延迟加载机制

延迟加载: 延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作.在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载.下面我们就分别介绍这些种类的延迟加载的细节. A.实体对象的延迟加载: 如果想对实体对象使用延迟加载,必须要在实体的映射配置文件中进行相应的配置,如下所示: <hibernate-mapping> <class name=”com.neus

Hibernate延迟加载机制详解

摘自 http://blog.chinaunix.net/uid-20577907-id-3129234.html 1 延迟加载: 延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作. 在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载.下面我们就分别介绍这些种类的延迟加载的细节. A.实体对象的延迟加载: 如果想对实体对象使用延迟加载,必须要在实体的映射配置文