本节内容

  • 引入
  • 延迟加载
  • 实例分析
    • 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 order0_.OrderId=@p0; @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 order0_.OrderId=@p0; @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]=@p0; @p0 = '2'

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

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

结语

这篇我们初步认识了NHibernate中的加载机制,这篇从一对多关系、多对多关系角度分析了NHibernate默认加载行为——延迟加载,下篇继续分析立即加载。希望对你有所帮助

NHibernate教程(12)--延迟加载的更多相关文章

  1. NODE-WEBKIT教程(12)全屏

    node-webkit教程(12)全屏 文/玄魂 目录 node-webkit教程(12)全屏 前言 12.1  和全屏有关的三个api Window.enterFullscreen() Window ...

  2. NHibernate教程

    NHibernate教程 一.NHibernate简介 在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦.浪费时间的.NHibernate是一个面向.Net环境的对象/关系数据库 ...

  3. NHibernate教程(13)--立即加载

    本节内容 引入 立即加载 实例分析 1.一对多关系实例 2.多对多关系实例 结语 引入 通过上一篇的介绍,我们知道了NHibernate中默认的加载机制--延迟加载.其本质就是使用GoF23中代理模式 ...

  4. [译]Vulkan教程(12)图形管道基础之入门

    [译]Vulkan教程(12)图形管道基础之入门 Introduction 入门 Over the course of the next few chapters we'll be setting u ...

  5. [转帖]Linux教程(12)- linux输入输出重定向

    Linux教程(12)- linux输入输出重定向 2018-08-21 22:57:02 钱婷婷 阅读数 49更多 分类专栏: Linux教程与操作 Linux教程与使用   版权声明:本文为博主原 ...

  6. 黑马lavarel教程---12、lavarel验证码

    黑马lavarel教程---12.lavarel验证码 一.总结 一句话总结: 用插件的时候仔细看插件的版本要求 1.lavarel安装验证码插件的时候,如果(可选)需要定义自己的配置,则需要生成配置 ...

  7. Directx11教程(12) 禁止alt+enter全屏窗口

    原文:Directx11教程(12) 禁止alt+enter全屏窗口        在D3D11应用程序中,我们按下alt+enter键,会切换到全屏模式.有时候,我们在WM_SIZE中有一些代码,全 ...

  8. 深度学习与CV教程(12) | 目标检测 (两阶段,R-CNN系列)

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...

  9. NHibernate教程(20)——二级缓存(上)

    本节内容 引入 介绍NHibernate二级缓存 NHibernate二级缓存提供程序 实现NHibernate二级缓存 结语 引入 上一篇我介绍了NHibernate内置的一级缓存即ISession ...

随机推荐

  1. 压缩[SCOI2007]

    题目描述 给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息.压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前 ...

  2. docke镜像上传到dockerhub仓库和阿里云docker仓库的方法

    操作指南   1.  登录阿里云docker registry: $ sudo docker login --username=linjiaxin897591495 registry.cn-hangz ...

  3. nopCommerce 3.9 大波浪系列 之 外部授权登录插件的开发实现

    一.简介 nop支持第三方外部授权登录的扩展,本篇通过编写微信公众平台登录插件进一步了解nop授权登录的开发过程. 微信公众平台.微信开放平台使用场景不一样,前者通过微信客户端进行开发如公众号,后者基 ...

  4. HTML 基本标签02

    02-html基本标签 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...

  5. MySQL中的基本知识

    本文是MySQL中的一些基本知识,包括各种基本操作.数据类型和表的约束等. ps:都是自己总结.手打出来的,虽然不是什么新东西,但也勉强算原创吧.. 一.数据库 1.创建:CREATE DATABAS ...

  6. mysql shutdown and kill

    --mysqladmin shutdown If you execute mysqladmin shutdown when connecting to a local server using a U ...

  7. Failed resolution of: Landroid/support/v7/internal/widget/TintManager;

    碰到这个问题,一般都是build.gradle中导入的包不匹配的问题,如TabLayout与ViewPager搭配使用的时候,必须保持导入包的版本一致 compile 'com.android.sup ...

  8. Python 写网络爬虫思路分析

    首先从程序入口开始分析,在程序入口处传入一个待爬取的网址, 使用下载器Html_downloader类下载该地址的内容,使用解释器 parser分析内容,利用BeautifulSoup包抓取想要爬取的 ...

  9. 神器Vim之命令介绍

    前天晚上看casa直播项目的组件化,不仅了解了如何快速对现有项目进行组件化,以及进一步不断迭代,实现完全组件化的过程:同时,也被casa的vim技能的熟练程度所震撼.早就听说,vim是unix系统上文 ...

  10. 用jlink在mini2440上烧写uboot

    首先,附上我安装jlink驱动: http://download.csdn.net/detail/zzmno1/3776716#comment 以及我使用的uboot.bin文件下载地址: http: ...