当前位置: 首页 > news >正文

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

本节内容

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

引入

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

立即加载

立即加载

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

实例分析

1.一对多关系实例

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

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

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

1.使用lazy="false"属性

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

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

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

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

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

SELECT customer0_.CustomerId as CustomerId9_0_,
       customer0_.Version as Version9_0_,
       customer0_.Firstname as Firstname9_0_,
       customer0_.Lastname as Lastname9_0_
FROM Customer customer0_ WHERE customer0_.CustomerId=@p0; @p0 = '1'

SELECT orders0_.Customer as Customer1_,
       orders0_.OrderId as OrderId1_,
       orders0_.OrderId as OrderId6_0_,
       orders0_.Version as Version6_0_,
       orders0_.OrderDate as OrderDate6_0_,
       orders0_.Customer as Customer6_0_
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对象集合。

public Customer EagerLoadUsingSessionAndNHibernateUtil(int customerId)
{
    using (ISession _session = new SessionManager().GetSession())
    {
        Customer customer= _session.Get<Customer>(customerId);
        NHibernateUtil.Initialize(customer.Orders);
        return customer;
    }
}

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

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

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

2.多对多关系实例

1.使用lazy="false"属性

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

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

2.使用NHibernateUtil实用类

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

public DomainModel.Entities.Order 
    EagerLoadOrderAggregateSessionAndNHibernateUtil(int orderId)
{
    using (ISession _session = new SessionManager().GetSession())
    {
        DomainModel.Entities.Order order =
            _session.Get<DomainModel.Entities.Order>(orderId);
        NHibernateUtil.Initialize(order.Customer);
        NHibernateUtil.Initialize(order.Products);
        return order;
    }
}

测试上面的方法:

[Test]
public void EagerLoadOrderAggregateSessionAndNHibernateUtilTest()
{
    Order order = 
        _relation.EagerLoadOrderAggregateSessionAndNHibernateUtil(2);
    Assert.IsTrue(NHibernateUtil.IsInitialized(order.Customer));
    Assert.IsTrue(NHibernateUtil.IsInitialized(order.Products));
    Assert.AreEqual(order.Products.Count, 2);
}

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

SELECT order0_.OrderId as OrderId6_0_,
       order0_.Version as Version6_0_,
       order0_.OrderDate as OrderDate6_0_,
       order0_.Customer as Customer6_0_
FROM [Order] order0_ WHERE order0_.OrderId=@p0; @p0 = '2'

SELECT customer0_.CustomerId as CustomerId9_0_,
       customer0_.Version as Version9_0_,
       customer0_.Firstname as Firstname9_0_,
       customer0_.Lastname as Lastname9_0_
FROM Customer customer0_ WHERE customer0_.CustomerId=@p0; @p0 = '1'

SELECT orders0_.Customer as Customer1_,
       orders0_.OrderId as OrderId1_,
       orders0_.OrderId as OrderId6_0_,
       orders0_.Version as Version6_0_,
       orders0_.OrderDate as OrderDate6_0_,
       orders0_.Customer as Customer6_0_
FROM [Order] orders0_ WHERE orders0_.Customer=@p0; @p0 = '1'

SELECT products0_.[Order] as Order1_1_,
       products0_.Product as Product1_,
       product1_.ProductId as ProductId8_0_,
       product1_.Version as Version8_0_,
       product1_.Name as Name8_0_,
       product1_.Cost as Cost8_0_
FROM OrderProduct products0_ 
left outer join Product product1_ on products0_.Product=product1_.ProductId
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立即取得那些原本需要延迟加载的属性。

下面写个简单例子说明:

public DomainModel.Entities.Order EagerLoadOrderAggregateWithHQL(int orderId)
{
    using (ISession _session = new SessionManager().GetSession())
    {
        return _session.CreateQuery("from Order o"+
            " left outer join fetch o.Products" +
            " inner join fetch o.Customer where o.OrderId=:orderId")
            .SetInt32("orderId", orderId)
            .UniqueResult<DomainModel.Entities.Order>();
    }
}

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

[Test]
public void EagerLoadOrderAggregateWithHQLTest()
{
    Order order = _relation.EagerLoadOrderAggregateWithHQL(2);
    Assert.IsTrue(NHibernateUtil.IsInitialized(order.Customer));
    Assert.IsTrue(NHibernateUtil.IsInitialized(order.Products));
    Assert.AreEqual(order.Products.Count, 2);
}

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

select order0_.OrderId as OrderId6_0_,
       product2_.ProductId as ProductId8_1_,
       customer3_.CustomerId as CustomerId9_2_,
       order0_.Version as Version6_0_,
       order0_.OrderDate as OrderDate6_0_,
       order0_.Customer as Customer6_0_,
       product2_.Version as Version8_1_,
       product2_.Name as Name8_1_,
       product2_.Cost as Cost8_1_,
       customer3_.Version as Version9_2_,
       customer3_.Firstname as Firstname9_2_,
       customer3_.Lastname as Lastname9_2_,
       products1_.[Order] as Order1_0__,
       products1_.Product as Product0__
from [Order] order0_
left outer join OrderProduct products1_ on order0_.OrderId=products1_.[Order]
left outer join Product product2_ on products1_.Product=product2_.ProductId
inner join Customer customer3_ on order0_.Customer=customer3_.CustomerId
where (order0_.OrderId=@p0 ); @p0 = '2'

SELECT orders0_.Customer as Customer1_,
       orders0_.OrderId as OrderId1_,
       orders0_.OrderId as OrderId6_0_,
       orders0_.Version as Version6_0_,
       orders0_.OrderDate as OrderDate6_0_,
       orders0_.Customer as Customer6_0_
FROM [Order] orders0_ WHERE orders0_.Customer=@p0; @p0 = '1'

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

结语

通过这篇和上一篇我们初步认识了NHibernate中的加载机制,依次从一对多关系、多对多关系角度分析了NHibernate默认延迟加载和立即加载。这些仅仅是我在平时应用、学习中摸索出来的一点收获,并非官方认可的东西,希望对你有所帮助。

本系列链接:NHibernate之旅系列文章导航

NHibernate Q&A
  • 欢迎加入NHibernate中文社区,一起讨论NHibernate知识!
  • 请到NHibernate中文社区下载本系列相关源码。

下次继续分享NHibernate!

转载于:https://www.cnblogs.com/lyj/archive/2008/10/29/1322373.html

相关文章:

  • C/C++中的函数参数传递机制(zz)
  • 使用Ajax 3.5开发的页面在IE6上访问的问题
  • wget常用参数 [转]
  • jQuery技巧总结(转)作为JQuery学习总结
  • 北京邮电大学微软技术俱乐部-移动开发小组第二次活动
  • [导入]厚度仅9.5mm的SSD硬盘问世
  • asp.net 生成缩略图、为图片添加文字水印、图片水印等功能
  • MiniMapX简介
  • 一个可以显示多边形的 TMyShape 类 - 回复 董勇 的问题
  • 邮件标头中找到无效字符(An invalid character was found in header value) System.Net.Mail 的Bug...
  • [导入]The Managed DirectX Render Loop
  • .Net(C#)自定义WinForm控件之小结篇
  • 并发(Concurrent)与并行(Parallel)
  • 产生title跟随效果
  • 优化MySQL数据库性能的八种方法
  • [译]CSS 居中(Center)方法大合集
  • 2017 前端面试准备 - 收藏集 - 掘金
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • CODING 缺陷管理功能正式开始公测
  • download使用浅析
  • HTTP 简介
  • HTTP--网络协议分层,http历史(二)
  • Java比较器对数组,集合排序
  • Js基础知识(一) - 变量
  • Laravel 中的一个后期静态绑定
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • select2 取值 遍历 设置默认值
  • Vue 重置组件到初始状态
  • webpack入门学习手记(二)
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 好的网址,关于.net 4.0 ,vs 2010
  • 区块链技术特点之去中心化特性
  • 使用权重正则化较少模型过拟合
  • 小李飞刀:SQL题目刷起来!
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 字符串匹配基础上
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 阿里云API、SDK和CLI应用实践方案
  • $().each和$.each的区别
  • $forceUpdate()函数
  • ()、[]、{}、(())、[[]]命令替换
  • (C语言)字符分类函数
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (三)mysql_MYSQL(三)
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (五)c52学习之旅-静态数码管
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .Net IE10 _doPostBack 未定义
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件