JPA定义实体之间的关系有如下几种:
@OneToOne
@ManyToOne
@OneToMany
@ManyToMany
在定义它们的时候可以通过fetch属性指定加载方式,有两个值:
FetchType.LAZY:延迟加载
FetchType.EAGER:急加载
急加载就好理解了,在加载一个实体的时候,其中定义是急加载的的属性(property)和字段(field)会立即从数据库中加载
开发过程中遇到问题最多的就是延迟加载,并且问题都是一个:
“为什么我定义为延迟加载了,但没起作用,相关的属性或者字段还是会立即加载出来?”
对于这个问题,我的理解是这样的,我们首先假设有如下的影射关系:
@Entity
@Table(name = "orders")
class Order{
@OneToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},fetch = FetchType.LAZY,mappedBy = "order")
private Collection
lineItems = new HashSet
();
@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY,mappedBy="order")
@JoinColumn(name="order_id")
private OrderPrice salePrice;
@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY)
@JoinColumn(name="customer_id")
private Customer customer;
}
@Entity
@Table(name = "order_items")
class LineItem{
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH},fetch = FetchType.LAZY)
@JoinColumn(name = "order_id",referencedColumnName = "order_id")
private Order order;
}
@Entity
@Table(name = "order_finance")
@AttributeOverride(name="id",column=@Column(name="order_id"))
class OrderPrice extends Price{
private Order order;
@OneToOne(cascade={},fetch=FetchType.LAZY)
@JoinColumn(name="order_id",referencedColumnName="order_id")
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
@MappedSupperclass
@Table(name = "order_finance")
class Price{
@Id
public Integer getId(){
...
}
}
表的关系是: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,映射的属性一定存在
hibernate FetchType理解的更多相关文章
-
对Hibernate的理解
---恢复内容开始--- 一.面向对象设计的软件内部运行过程可以理解成就是不断创建各种新对象,建立对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程.不管程序运行的过程和操作怎么样,本 ...
-
hibernate知识点理解
1.只有业务逻辑层出现的问题? 1.切换数据库麻烦 2.sql编写起来麻烦 3.我们的程序员不需要关注数据库,只希望关心业务本身 2.hibernate的好处 1.程序员只关心业务逻辑,使角色更加清楚 ...
-
初识Hibernate之理解持久化类
上一篇文章我们简单介绍了Hibernate相关的一些最基本的文件及其作用,并在最后完整的搭建了Hibernate的运行环境,成功的完成了与数据库的映射.但是至于其中的一些更加细节的地方并没有 ...
-
谈谈你对Hibernate的理解
答: 1. 面向对象设计的软件内部运行过程可以理解成就是在不断创建各种新对象.建立对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程,不管程序运行的过程和操作怎么样,本质上都是要得到一 ...
-
Hibernate基本原理理解
什么是Hibernate? Hibernate,翻译过来是冬眠的意思,正好现在已经进入秋季,世间万物开始准备冬眠了.其实对于对象来说就是持久化. 扫盲------------------------- ...
-
谈谈对hibernate的理解
它是ORM思想的一个实现,对JDBC进行了很好的封装,它通过配置使JavaBean对象和数据库表之间进行映射,并提供对增.删.改.查便利的操作方法,同时支持事务处理,它对数据库记录还提供了缓存机制,提 ...
-
Java ssh 框架 hibernate 详细理解
Hibernate框架技术相信对大多数的 java 程序员并不陌生,数据表之间的关系如何通过Hibernate来建立,需要我们认真的分析数据表中数据项之间的交互: 数据库表的之间的关系有: (1)一对 ...
-
hibernate 事务理解
简介: Hibernate本身并不具备事务管理能力 .在事务管理层, Hibernate将其委托给底层的JDBC或者JTA ,以实现事务管理和调度功能. Hibernate的默认事务处理机制基于JDB ...
-
hibernate的理解
emm~这里就是记录一下,hibernate的save,如果存在id,就可以直接save,就会覆盖原有的,如果不存在id就会在数据库创建一条新的记录 package edu.zut.cs.zutnlp ...
随机推荐
-
[原] KVM 虚拟化原理探究(3)— CPU 虚拟化
KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...
-
magento中的ajax
<script type="text/javascript"> function loadRecommend(){ $.ajax({ ...
-
[Unity3D]Unity3D游戏开发之在3D场景中选择物体并显示轮廓效果
大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei. 在<仙剑奇侠传>.<古剑奇谭>等游戏中,常常须要玩家在一个3D场景中 ...
-
oc-08-内存分析
说有对象公用类的一个方法
-
python学习笔记三--字典的使用
一.基本使用: 1. 赋值:{key:value} 1.1 与列表相同处:会改变索引(键)相关联的值的改变 1.2 与列表不同处:不用考虑值的长度,而列表是有序的需要考虑末尾偏移量,超过末尾偏移量的会 ...
-
webrtc学习———记录二:canvas学习
参考资料: http://bucephalus.org/text/CanvasHandbook/CanvasHandbook.html#getcontext2d https://developer.m ...
-
ibatis学习之道:ibatis的<;[CDATA]>;dynamic属性跟#$的应用
ibatis的<![CDATA]>,dynamic属性和#,$的应用 <![CDATA[ ]]>的正确使用 ibatis作为一种半自动化的OR Mapping工具,其灵活性 ...
-
gen_server terminate与trap_exit
不论是新手还是熟手,写gen_server时常会遇到terminate/2,有时执行,有时却不执行的困惑. 比如*中的Handling the cleanup of the g ...
-
最近面试 Java 后端开发的感受!
阅读本文大概需要 4.3 分钟. 首发:cnblogs.com/JavaArchitect/p/10011253.html 上周,密集面试了若干位Java后端候选人,工作经验在3到5年间.我的标准其实 ...
-
Windows平台下tomcat 性能调优
Tomcat 线程查看工具: https://blog.csdn.net/jrainbow/article/details/49026365 16G内存 Tomcat并发优化.内存配置.垃圾回收.宕机 ...