`
gaowenming
  • 浏览: 162495 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

一对一延迟加载问题探讨

阅读更多

JPA定义实体之间的关系有如下几种:

在定义它们的时候可以通过fetch属性指定加载方式,有两个值:

FetchType.LAZY:延迟加载 FetchType.EAGER:急加载

急加载就好理解了,在加载一个实体的时候,其中定义是急加载的的属性(property)和字段(field)会立即从数据库中加载 开发过程中遇到问题最多的就是延迟加载,并且问题都是一个:

“为什么我定义为延迟加载了,但没起作用,相关的属性或者字段还是会立即加载出来?”

对于这个问题,我的理解是这样的,我们首先假设有如下的影射关系:

表的关系是:orders表是一个单独的表,order_items表有一个外键(order_id)引用到orders表,order_finance 有一个外键(order_id)引用到orders表. order_items------->orders<------------order_finance

customer

现在的问题就是:
Order.lineItems 这个@OneToMany的LAZY延迟加载是起作用 的,find order的时候没有find出lineItems
Order.customer 这个@OneToMany的LAZY延迟加载是起作用 的,find order的时候没有find出Customer
Order.salePrice 这个@OneToOne的LAZY延迟加载没起作用 ,find order后会把相关的OrderPrice也fetch 出来
LineItem.order 这个@ManyToOne的LAZY延迟加载是起作用 的,find lineitem没有把相关的order find出来
OrderPrice.order 这个@OneToOne的LAZY延迟加载没起作用 ,find orderprice的时候把相关的order find出来了

延迟加载,顾名思义,就是在访问具体的属性时才从数据库中加载,比如例子中,只有调用OrderPrice.getOrder()的时候才应该会加载 Order这个实体,加载OrderPrice的时候是不应该加载Order的。

那么首先想想,对于延迟加载,hibernate怎么知道什么时候会调用到相关的实体的get方法呢?
答案是它不知道,hibernate不知道什么时候会调用到相关的get方法,那么hibernate如何实现只有访问到才加载这一点? hibernate使用了代理(Proxy),对实体的调用会被代理接受和处理,hibernate可以设置这个代理被调用到的时候去加载数据,从而实现 延迟加载。那么对于一个映射对象,要么它有值,要么它是null,对于null值建立代理是没多大作用的,而且也不能对null建立动态代理。那就是说 hibernate在对延迟加载建立代理的时候要考虑这个映射的对象是否是null。如果是null不需要建立代理,直接把映射的值设置成null,如果 映射的对象不为null,那么hibernate就建立代理对象

延迟加载失败都是由于确定映射的内容是否是null引起的 先来看@OneToMany,比如例子中的Order.lineitems,这是一个Collection,hibernate在加载Order的时候不 加载lineitems,而是创建一个代理(Proxy)一个针对Collection的代理(通常是org.hibernate.collection.persistentBag 。除非你调用了像 Order.getLineItems.size()或者Order.getLineItems.get()方法的时候hibernate才会去加载这个 order的lineitems数据,要不然只是调用Order.getLineItems是不会加载到数据的,因为这个时候并没有具体的访问 LineItem. 由于代理是针对Collection建立的,而不是针对实体建立的,hibernate不用太多考虑是否为null,如果lineitem没有,也只是代 表这个集合是长度是0,这个集合是不为Null的。所以这很容易实现延迟加载

现在在来看例子@OneToOne Order.salePrice。它为什么会失败呢? hibernate也会建立代理,但这个代理是针对OrderPrice建立的(如果延迟加载成功,这个代理类形如 Customer_javasisst_$1) ,默认optioanl=true,也就是说OrderPrice可以为 null,那么hibernate就要考虑,这里是放一个null呢?还是放一个代理。但在Order这个实体里是不能确定它有没有价格的(但在价格里知 道他的Order,有个外键指向order),所以hibernate要确认这个OrderPrice是否存在,这个确认就导致的延迟加载失败,因为 OrderPrice要被查询一次,如果不存在映射值为null,如果存在这个时候值都取出来了,当然就不用什么代理了

Order.customer延迟加载是成功的,order表有一个外键关联到customer表,hibernate应该从这里知道这个 customer是确实存在的,不用把映射值设置成null了,可以设置成代理类Customer_javasisst_$2

那如果把Order.salePrice的映射定义修改成:
@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY,optional=false ,mappedBy="order") @JoinColumn(name="order_id")
private OrderPrice salePrice;
延迟加载就成功了,因为optional=false 指 定salePrice不是可选的,必须有值,所以hibernate也不用考虑是该放null还是放代理,既然必须有值,又是延迟加载,那就设置成代理类 了

根据上面所说的,OrderPrice定义有一个外键关联到Order,那OrderPrice.order这个延迟加载应该是成功的,但为什么会失败 呢? 难道是Order与OrderPrice两边都定义了OneToOne关系? 我这个例子中,这里失败我想是因为OrderPrice这个实体的定 义:@AttributeOverride(name="id",column=@Column(name="order_id"))

再来看看ManyToOne的LineItem.order,这个延迟加载也是成功的。因为lineitem定义了外健关系到order 对于延迟加载的对象,如果已经脱离了容器,调用会得到org.hibernate.LazyInitializationException: could not initialize proxy - no Session方法异常

还有一种情况下延迟加载“看起来是没起作用的”:其实是起作用的,但可能在什么地方的代码调用到了相关的get方法,把延迟加载的对象加载出来的,所以看 起来是没有成功的

总结:

  • 对于延迟加载,hibernate无法知道什么时候会调用到延迟加载的属性/字段的get方法,所以对于延迟加载的属性/字段,hibernate会通过 建立代理Proxy来包装(Wrapper)一下
  • 代理可能会根据实体本身建立,也可以是根据一个集合建立,如果是根据一个集合建立,延迟加载一般都能成功,如果是根据实体建立,null是不能建立代理 的,如果能够确定代理类一定存在,那延迟加载就能成功,相关的映射放置的是代理类,如果不能确定映射的属性是否存在,那就会去数据库中进行查询,这就导致 的延迟失败。 外键定义可以让hibernate知道映射的属性是否存在 也可以通过optional=false来告诉hibernate,映射的属性一定存在
分享到:
评论
3 楼 icantforget 2011-12-12  
有问题啊  optional=false 强制生成对象? 延迟加载?那你的id是怎么来的?
2 楼 gaowenming 2011-07-23  
liguirong98 写道
学习了,但如果该属性确定可能为空呢?那不能设optional=false,怎么办呢?

那就optional=true, 表示该属性可能为空,那就无法延迟加载了,在查询的时候就会去确定该属性的值!
1 楼 liguirong98 2011-07-22  
学习了,但如果该属性确定可能为空呢?那不能设optional=false,怎么办呢?

相关推荐

    Android中ViewPager+Fragment懒加载问题解决方案.zip

    Android中ViewPager+Fragment取消(禁止)预加载延迟加载(懒加载)问题解决方案 方案是为解决特定问题或达成特定目标而制定的一系列计划或步骤。它的作用是提供一种系统性的方法,以有效地应对挑战、优化流程或实现目标...

    js实现延迟加载的几种方法详解

    这是一个面试经常问到的问题:js的延迟加载方法 (js的延迟加载有助于提高页面的加载速度) 主要考察对程序的性能方面是否有研究,程序的性能是一个项目不断地追求的,通常也是项目完成后需要长期做的一件事情,像腾讯...

    Python方法的延迟加载的示例代码

    数据挖掘的过程中,数据进行处理是一重要的环节,我们往往会将其封装成一个方法,而有的时候这一个方法可能会被反复调用,每一次都对数据进行处理这将是一个很耗时耗资源的操纵,那么有没有办法将计算后的结果 缓存 ...

    C#程序实现动态调用DLL的研究

    介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资源中释放出来,通过静态加载延迟实现DLL函数的动态加载,程序退出后实现临时文件的自动删除,从而为解决“DLL Hell”提供...

    基于HTML5技术的产品演示系统设计与开发

    在对传统Demo演示系统的改进方面,我的工作主要包括为提供产品演示系统的移动平台支持,开发了专门的文件缓存机制,解决了用户使用产品演示系统中网络掉线所导致的演示Demo中断,同时也在演示Demo中加入了预加载功能,...

    皮肤控件研究文档,破解后的库文件,皮肤设计工具使用教程

    界面库产品应该对图片等资源加载做优化,以尽可能的减少界面库加载等带来的时间开销。也可以通过比较软件启动速度来 比较界面库的效率。  软件色调调整时的效率 色调调整等操作一般会涉及到整体界面库的运算与...

    爆炸荷载对邻近巷道背爆侧裂纹的影响规律

    为了探究爆炸应力波对邻近巷道背爆侧隐性裂纹的影响规律,采用爆炸加载透射式动态焦散线系统,进行了试验研究。试验结果表明:在爆炸荷载作用下,邻近巷道围岩中存在裂纹缺陷的背爆侧也成为主要扰动区,且扰动大小与裂纹...

    夏昕.深入浅出Hibernate

    从一个基础程序入手,讲述Hibernate的基本语法与配置,慢慢升高到缓存、延迟加载等高级特性。本书内容深入浅出,先讲述持久层设计与ORM,再由Hibernate概述、Hibernate基础Hibernate高级特性顺序展开,直至Hibernate...

    将Web应用性能提高十倍的10条建议

    另一个最近的研究特别强调一个事实,即超过一半的网站拥有者在调查中承认它们会因为应用程序性能的问题流失用户。网站到底需要多快呢?对于页面加载,每增加1秒钟就有4%的用户放弃使用。顶级的电

    Java虚拟机

    第一部分从宏观的角度介绍了整个Java技术体系、Java和JVM的发展历程、模块化,以及JDK的编译,这对理解本书后面内容有重要帮助。第二部分讲解了JVM的自动内存管理,包括虚拟机内存区域的划分原理以及各种内存溢出...

    立体化网络应用层协议识别的研究与实现

    针对由于动态端口和加密通信协议的不断使用、应用层协议识别难度增加以及对高速网络的快速识别需求提升的问题,提出了一种立体化的应用层协议识别方法。其依据树形分类思想,根据数据包的多元信息进行分层过滤识别。...

    java开源包101

    Chronicle 是一个超低延迟、高吞吐、持久化的消息和事件驱动的内存数据库,延迟只有16纳秒以及支持每秒钟 500-2000 万消息/记录。 google-api-translate-java(Java 语言对Google翻译引擎的封装类库) 语音识别程序 ...

    windows驱动开发技术详解-part2

     本章将带领读者一步步对驱动程序进行编译、安装和简单的调试工作。这些步骤虽然简单,但往往困 惑着初次接触驱动程序的开发者。  3.1 用C语言还是用C++语言  3.1.1 调用约定  3.1.2 函数的导出名  3.1.3 ...

    Windows驱动开发技术详解的光盘-part1

    本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,而且介绍了编程技巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导意义,是一本值得推荐的...

    jQuery基础教程(第四版)

    第13章将带领读者深入理解Ajax相关的概念,包括jQuery的延迟处理机制,从而实现等待数据在一段时间后可用时再对其进行处理。 附录A将帮助读者理解闭包——什么是闭包,怎么利用闭包。 附录B向读者介绍使用jQUnit库对...

Global site tag (gtag.js) - Google Analytics