本节内容

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

引入

通过上一篇的介绍,我们知道了NHibernate中默认的加载机制——延迟加载。其本质就是使用GoF23中代理模式实现,这节我们简单分析NHibernate另一种加载机制——立即加载。我用一张图片形象的展现立即加载机制。

立即加载

顾名思义,就是立刻加载相关联对象集合,与延迟加载相反。我们可以使用三种方法来立即加载,分别是:可选的lazy属性、NHibernate提供的实用类、HQL抓取策略。下面依次用实例分析其中的机制。

实例分析

1.一对多关系实例

在一对多关系实例中,我们使用Customer对象与Order对象为例,在数据访问层中依然使用上一篇的方法,这里使用强制关闭Session的方法,为什么使用Using强制释放资源呢?我就是想利用这个来模拟Web应用程序中的Session机制。用这个分析比没有Using释放资源更有意义。

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

  1. public Customer LazyLoadUsingSession(int customerId)
  2. {
  3. using (ISession _session = new SessionManager().GetSession())
  4. {
  5. return _session.Get<Customer>(customerId);
  6. }
  7. }

1.使用lazy="false"属性

在上一篇我们一直没有修改映射文件即一直默认是lazy="true",NHibernate就采用了默认的延迟加载。

这里介绍第一种方法就是修改映射文件来立即加载,打开Customer.hbm.xml文件,在Set元素中添加lazy="false"。

编写一个测试验证,调用数据访问层中的使用Using强制资源清理Session加载Customer对象的方法加载一个Customer对象,NHibernate这时立即加载Customer相关联的Order对象。利用NHibernate提供实用类(NHibernateUtil)测试被关联的Customer对象集合是否已初始化(也就是已加载)。

  1. [Test]
  2. public void EagerLoadUsingLazyFalseTest()
  3. {
  4. Customer customer = _relation.LazyLoadUsingSession(1);
  5. Assert.IsTrue(NHibernateUtil.IsInitialized(customer.Orders));
  6. }

测试成功,证明NHibernate立即加载了Order对象,发现生成两句SQL语句:第一条查询Customer对象,第二条语句查询其相关联的Order对象集合。

  1. SELECT customer0_.CustomerId as CustomerId9_0_,
  2. customer0_.Version as Version9_0_,
  3. customer0_.Firstname as Firstname9_0_,
  4. customer0_.Lastname as Lastname9_0_
  5. FROM Customer customer0_ WHERE customer0_.CustomerId=@p0; @p0 = '1'
  6.  
  7. SELECT orders0_.Customer as Customer1_,
  8. orders0_.OrderId as OrderId1_,
  9. orders0_.OrderId as OrderId6_0_,
  10. orders0_.Version as Version6_0_,
  11. orders0_.OrderDate as OrderDate6_0_,
  12. orders0_.Customer as Customer6_0_
  13. FROM [Order] orders0_ WHERE orders0_.Customer=@p0; @p0 = '1'

不过,细心的朋友会发现,这时Orders对象集合的类型是Iesi.Collections.Generic.HashedSet`1[DomainModel.Entities.Order],上一节只有在没有使用Using强制关闭资源下,Orders对象集合才是这个类型,在使用强制关闭资源的情况下,Orders对象集合的类型为:NHibernate.Collection.Generic.PersistentGenericSet<DomainModel.Entities.Order> ,进一步读取Order项抛出HibernateException异常。我想从这个角度也说明了立即加载机制。

好了,这就说到这里,还是把映射文件改为原来默认的吧(即去掉lazy="false"),看看还有其它什么方法来立即加载。

2.使用NHibernateUtil实用类

NHibernate提供实用类(NHibernateUtil)不光光只是用来测试被关联的对象集合是否已初始化,还有一个非常重要的功能就是可以强制初始化未初始化的相关联的对象。有了这个功能,我们就可以修改数据访问层中的方法,把上面使用Using强制清理关闭Session的方法中加上NHibernateUtil类提供Initialize方法来初始化Customer相关联的Order对象集合。

  1. public Customer EagerLoadUsingSessionAndNHibernateUtil(int customerId)
  2. {
  3. using (ISession _session = new SessionManager().GetSession())
  4. {
  5. Customer customer= _session.Get<Customer>(customerId);
  6. NHibernateUtil.Initialize(customer.Orders);
  7. return customer;
  8. }
  9. }

我们编写一个方法来测试一下:

  1. [Test]
  2. public void EagerLoadUsingSessionAndNHibernateUtilTest()
  3. {
  4. Customer customer = _relation.EagerLoadUsingSessionAndNHibernateUtil(1);
  5. Assert.IsTrue(NHibernateUtil.IsInitialized(customer.Orders));
  6. }

测试成功,这个结果同修改映射文件一样。

2.多对多关系实例

1.使用lazy="false"属性

同理,使用lazy="false"属性来设置立即加载行为,这时在持久化类中就不必为其公共方法、属性和事件声明为virtual属性了,因为没有使用延迟加载。不过在这里我还是推荐大家使用NHibernate默认的延迟加载行为,原因很简单,NHibernate延迟加载性能上可以提高很多,在特殊情况下使用下面的方法来立即加载。

这个例子同上面类似,这里就不举重复的例子了,大家自己测试下就可以了。

2.使用NHibernateUtil实用类

如果你需要获得Order实体的相关联对象可以使用NHibernateUtil类初始化关联对象(把他们从数据库取出来)。看看下面数据访问层中的方法,使用NHibernateUtil类提供Initialize方法初始化相关联的Customer和Product对象。

  1. public DomainModel.Entities.Order
  2. EagerLoadOrderAggregateSessionAndNHibernateUtil(int orderId)
  3. {
  4. using (ISession _session = new SessionManager().GetSession())
  5. {
  6. DomainModel.Entities.Order order =
  7. _session.Get<DomainModel.Entities.Order>(orderId);
  8. NHibernateUtil.Initialize(order.Customer);
  9. NHibernateUtil.Initialize(order.Products);
  10. return order;
  11. }
  12. }

测试上面的方法:

  1. [Test]
  2. public void EagerLoadOrderAggregateSessionAndNHibernateUtilTest()
  3. {
  4. Order order =
  5. _relation.EagerLoadOrderAggregateSessionAndNHibernateUtil(2);
  6. Assert.IsTrue(NHibernateUtil.IsInitialized(order.Customer));
  7. Assert.IsTrue(NHibernateUtil.IsInitialized(order.Products));
  8. Assert.AreEqual(order.Products.Count, 2);
  9. }

看看NHibernate生成的SQL语句,真是多了,一对多关系,多对多关系的一次立即加载就生成了四条SQL语句,分别查询了Order表,Customer表,OrderProduct表相关联的Product。(Customer与Order一对多关系在这里也立即加载了一次),这时内存中的内容都是这些关联对象的值,你也不是每个对象都用到,何必要全部加载呢。

  1. SELECT order0_.OrderId as OrderId6_0_,
  2. order0_.Version as Version6_0_,
  3. order0_.OrderDate as OrderDate6_0_,
  4. order0_.Customer as Customer6_0_
  5. FROM [Order] order0_ WHERE order0_.OrderId=@p0; @p0 = '2'
  6.  
  7. SELECT customer0_.CustomerId as CustomerId9_0_,
  8. customer0_.Version as Version9_0_,
  9. customer0_.Firstname as Firstname9_0_,
  10. customer0_.Lastname as Lastname9_0_
  11. FROM Customer customer0_ WHERE customer0_.CustomerId=@p0; @p0 = '1'
  12.  
  13. SELECT orders0_.Customer as Customer1_,
  14. orders0_.OrderId as OrderId1_,
  15. orders0_.OrderId as OrderId6_0_,
  16. orders0_.Version as Version6_0_,
  17. orders0_.OrderDate as OrderDate6_0_,
  18. orders0_.Customer as Customer6_0_
  19. FROM [Order] orders0_ WHERE orders0_.Customer=@p0; @p0 = '1'
  20.  
  21. SELECT products0_.[Order] as Order1_1_,
  22. products0_.Product as Product1_,
  23. product1_.ProductId as ProductId8_0_,
  24. product1_.Version as Version8_0_,
  25. product1_.Name as Name8_0_,
  26. product1_.Cost as Cost8_0_
  27. FROM OrderProduct products0_
  28. left outer join Product product1_ on products0_.Product=product1_.ProductId
  29. WHERE products0_.[Order]=@p0; @p0 = '2'

3.使用HQL抓取策略

使用HQL查询方法也可以立即加载。HQL语句支持的连接类型为:inner join(内连接)、left outer join(左外连接)、right outer join(右外连接)、full join(全连接,不常用)。

“抓取fetch”连接允许仅仅使用一个选择语句就将相关联的对象随着他们的父对象的初始化而被初始化,可以有效的代替了映射文件中的外联接与延迟属性声明。

几点注意:

  • fetch不与setMaxResults() 或setFirstResult()共用,因为这些操作是基于结果集的,而在预先抓取集合时可能包含重复的数据,也就是说无法预先知道精确的行数。
  • fetch还不能与独立的with条件一起使用。通过在一次查询中fetch多个集合,可以制造出笛卡尔积,因此请多加注意。对多对多映射来说,同时join fetch多个集合角色可能在某些情况下给出并非预期的结果,也请小心。
  • 使用full join fetch 与 right join fetch是没有意义的。 如果你使用属性级别的延迟获取,在第一个查询中可以使用 fetch all properties 来强制NHibernate立即取得那些原本需要延迟加载的属性。

下面写个简单例子说明:

  1. public DomainModel.Entities.Order EagerLoadOrderAggregateWithHQL(int orderId)
  2. {
  3. using (ISession _session = new SessionManager().GetSession())
  4. {
  5. return _session.CreateQuery("from Order o"+
  6. " left outer join fetch o.Products" +
  7. " inner join fetch o.Customer where o.OrderId=:orderId")
  8. .SetInt32("orderId", orderId)
  9. .UniqueResult<DomainModel.Entities.Order>();
  10. }
  11. }

编写测试用例测试上面的方法:验证构建一个HQL查询不仅加载Order,也加载了相关联的Customer和Product对象。

  1. [Test]
  2. public void EagerLoadOrderAggregateWithHQLTest()
  3. {
  4. Order order = _relation.EagerLoadOrderAggregateWithHQL(2);
  5. Assert.IsTrue(NHibernateUtil.IsInitialized(order.Customer));
  6. Assert.IsTrue(NHibernateUtil.IsInitialized(order.Products));
  7. Assert.AreEqual(order.Products.Count, 2);
  8. }

通过NHibernate生成SQL语句可以说明NHibernate可以一口气立即加载Order和所有Order相关联的Customer和Product对象。SQL语句生成如下:

  1. select order0_.OrderId as OrderId6_0_,
  2. product2_.ProductId as ProductId8_1_,
  3. customer3_.CustomerId as CustomerId9_2_,
  4. order0_.Version as Version6_0_,
  5. order0_.OrderDate as OrderDate6_0_,
  6. order0_.Customer as Customer6_0_,
  7. product2_.Version as Version8_1_,
  8. product2_.Name as Name8_1_,
  9. product2_.Cost as Cost8_1_,
  10. customer3_.Version as Version9_2_,
  11. customer3_.Firstname as Firstname9_2_,
  12. customer3_.Lastname as Lastname9_2_,
  13. products1_.[Order] as Order1_0__,
  14. products1_.Product as Product0__
  15. from [Order] order0_
  16. left outer join OrderProduct products1_ on order0_.OrderId=products1_.[Order]
  17. left outer join Product product2_ on products1_.Product=product2_.ProductId
  18. inner join Customer customer3_ on order0_.Customer=customer3_.CustomerId
  19. where (order0_.OrderId=@p0 ); @p0 = '2'
  20.  
  21. SELECT orders0_.Customer as Customer1_,
  22. orders0_.OrderId as OrderId1_,
  23. orders0_.OrderId as OrderId6_0_,
  24. orders0_.Version as Version6_0_,
  25. orders0_.OrderDate as OrderDate6_0_,
  26. orders0_.Customer as Customer6_0_
  27. FROM [Order] orders0_ WHERE orders0_.Customer=@p0; @p0 = '1'

通过使用HQL抓取策略可以很好的在程序中编写出自己想要的结果。

[转]NHibernate之旅(13):初探立即加载机制的更多相关文章

  1. nodejs(13)模块加载机制

    模块加载机制 优先从缓存中加载 当一个模块初次被 require 的时候,会执行模块中的代码,当第二次加载相同模块的时候,会优先从缓存中查找,看有没有这样的一个模块! 好处:提高模块的加载速度:不需要 ...

  2. webpack入坑之旅(五)加载vue单文件组件

    这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新.下面是目前完成的列表: webpack入坑之旅(一)不 ...

  3. WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探

    原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探         最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感 ...

  4. 使用CEfSharp之旅(1) 加载网络页面

    原文:使用CEfSharp之旅(1) 加载网络页面 版权声明:本文为博主原创文章,未经博主允许不得转载.可点击关注博主 ,不明白的进群191065815 我的群里问 https://blog.csdn ...

  5. Dojo初探之1:AMD规范,编写符合AMD规范(异步模块加载机制)的模块化JS(其中dojo采用1.11.2版本)

    一.AMD规范探索 1.AMD规范(即异步模块加载机制) 我们在接触js的时候,一般都是通过各种function来定义一些方法,让它们帮我们做一些事情,一个js可以包含很多个js,而这些functio ...

  6. Android 的 so 文件加载机制

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 最近碰到一些 so 文件问题,顺便将相关知识点梳理一下. 提问 本文的结论是跟着 System.loadlibrary() 一层层源 ...

  7. PHP autoload与spl_autoload自动加载机制的深入理解

    PHP autoload与spl_autoload自动加载机制的深入理解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-05我要评论 本篇文章是对PHP中的autoload与spl_ ...

  8. 插件化框架解读之so 文件加载机制(四)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 提问 本文的结论是跟着 System.loadlibrary() ...

  9. thinkphp5源码剖析系列1-类的自动加载机制

    前言 tp5想必大家都不陌生,但是大部分人都停留在应用的层面,我将开启系列随笔,深入剖析tp5源码,以供大家顺利进阶.本章将从类的自动加载讲起,自动加载是tp框架的灵魂所在,也是成熟php框架的必备功 ...

随机推荐

  1. JSON Date Format/JSON 日期格式方法分享

    我是很懒的,不想多说,所以直接上代码.亲们懂的. <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://w ...

  2. GPS坐标转百度地图并且加载地图示例.支持微信端访问

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Wx_VehicleLoca ...

  3. 排队(BZOJ1731:[Usaco2005 dec]Layout 排队布局)

    [问题描述] Czy喜欢将他的妹子们排成一队.假设他拥有N只妹纸,编号为1至N.Czy让他们站成一行,等待自己来派送营养餐.这些妹纸按照编号大小排列,并且由于它们都很想早点吃饭,于是就很可能出现多只妹 ...

  4. C++学习笔记37:元编程

    元编程 什么是元编程(metaprogramming) 利用模板可以进行编译期计算(数值计算,型式计算和代码计算)的特点进行程序设计 为什么可以进行元编程? C++是两层语言:执行编译期计算的代码称为 ...

  5. iOS8上放大缩小的动画

    CGAffineTransformMakeScale这个方法我们以前经常使用,但是在IOS8上出现问题了 [UIView animateWithDuration:0.3 animations:^{ b ...

  6. ios短信和电话--参考

    调用打电话功能 [[UIApplicationsharedApplication] openURL:[NSURL URLWithString:@"tel://10086"]]; 调 ...

  7. vs运行代码版本不一致删除缓存

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files

  8. BZOJ 3955 Surely You Congest 解题报告

    首先,我们可以求出源为 $1$ 号点的最短路图以及各个点到 $1$ 号点的最短路. 然后我们考虑那些距离不同的点,是一定不会发生拥堵现象的. 然后我们就只需要考虑那些距离相同的点,就相当于做一个最大流 ...

  9. [CC150] Get all permutations of a string

    Problem: Compute all permutations of a string of unique characters. 此题用循环的方式不好做,下面是一种递归的思路: 把给的字符串看成 ...

  10. UVA 11733 Airports

    最小生成树,然后看他有多少个连通分量,每个连通分量有个飞机场,最后看所有剩下的边是否有大于飞机场的费用,有的话,改成飞机场: 比赛的时候一直没想明白,╮(╯▽╰)╭: #include<cstd ...