来源:http://www.diwuzhang.com/people-1/article-124/

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,映射的属性一定存在

关于JAP FetchType.LAZY(hibernate实现)的理解相关推荐

  1. hibernate 配置@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY) 主导方问题删除问题。

    hibernate 配置@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)  ?? 多对一的关系 ,关系有一个主导方.主导方应该是one的 ...

  2. FetchType.LAZY和FetchType.EAGER什么区别?(懒加载和急加载的理解)

    1.FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载. 2.FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载. ...

  3. Java Persistence API中的FetchType LAZY和EAGER之间的区别?

    我是Java Persistence API和Hibernate的新手. Java Persistence API中的FetchType.LAZY和FetchType.EAGER什么区别? #1楼 我 ...

  4. fetchtype 动态控制_hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法

    对这种懒加载问题,最后的做法是利用Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个Session,使得Hi ...

  5. Hibernate--fetch=FetchType.LAZY

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="subject") @JoinColumn(na ...

  6. fetchtype 动态控制_hibernate 关于 注解配置@Basic(fetch=FetchType.LAZY) 不起效果

    intro字段配置成@Basic(fetch=FetchType.LAZY) 但是无论用get还是load方法,intro和其他字段都是一样加载的,和其他字段没有区别啊?配了和没配是一样的? 有没有人 ...

  7. 让Spring 3中jsp的数据对象使用懒加载(FetchType.LAZY)与Controller的JSR 303并存

    本文出处:http://blog.csdn.net/chaijunkun/article/details/9083171,转载请注明.由于本人不定期会整理相关博文,会对相应内容作出完善.因此强烈建议在 ...

  8. FetchType.LAZY和FetchType.EAGER什么区别

    1.FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载.2.FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载.3. ...

  9. Spring,FetchType.LAZY和FetchType.EAGER什么区别?

    1.FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载. 2.FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载. ...

最新文章

  1. linux独立应用程序开发,Linux应用程序开发(一)
  2. C++模式学习------策略模式
  3. tomcat线程释放时间_聊下并发和Tomcat线程数(错误更正)
  4. 【原】高清显示屏原理及设计方案
  5. C语言常见的错误类型
  6. 如何使用IDEA 显示一个类的所有方法?与eclipse的outline视图类似
  7. appium--每次启动会重新安装的问题(没试过)
  8. python处理表格数据教程_python利用Excel读取和存储测试数据完成接口自动化教程...
  9. 基于C++与VS2012的HDF5文件处理(一)
  10. 错误 C2628 “Wall”后面接“int”是非法的(是否忘记了“;”?)
  11. python发音模块-python 利用pyttsx3文字转语音过程详解
  12. python 多个装饰器的调用顺序
  13. CSS——简写属性(在padding和margin这样的简写属性中,值赋值的顺序是top、right、bottom、left)...
  14. 计算机电工电子怎么学,计算机在电子电工课中的应用
  15. 我的未来作文计算机行业,我的未来职业作文4篇
  16. 【渝粤教育】国家开放大学2019年春季 2444酒店管理概论 参考试题
  17. jpi多表联查_使用mybatis进行多表联查
  18. java中insteadof_Java代码规范小结(一)
  19. 多吃巧克力多笑脑子会更聪明
  20. android 相机功能修改,Android11不再支持更改默认相机程序,再也不能用美颜相机拍照了...

热门文章

  1. GetWindowText和GetDlgItemText的区别
  2. EveryEeek English(9.21)
  3. SQL 时间默认值设置问题
  4. C/C++注册表【2】打开,创建,删除
  5. C#sql帮助类(登录查询界面)语句实例
  6. swift5 字符串格式化保留2 位,缺0自动补0
  7. Swift5关于根据一个frame得到最大X值和最大Y值
  8. led内部结构_科普PCB,DPC,陶瓷PCB对于LED封装有哪些差异? - led显示屏_高清led显示屏价格_led显示屏生产厂家...
  9. opencvsharp中resize图像
  10. myeclipse springboot 运行内存溢出_springboot学习心得 - aowumao