zoukankan      html  css  js  c++  java
  • hibernate FetchType理解

     

    hibernate FetchType理解

    分类: Hibernate

    转载于:http://hi.baidu.com/silencefool/blog/item/bc3443efd66538d8b31cb1c2.html

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

  • 相关阅读:
    获取字符串的MD5值
    将对象XML序列化为XML文件/反序列化XML文件为对象
    C#通过反射获得对象所有属性和值
    Jquery.Validate使用
    JS删除确认框
    生成验证码
    文件备份方法
    Log日志类
    Ext.NET 基础学习笔记08 (FormPanel)
    Tibco EMS Message trace
  • 原文地址:https://www.cnblogs.com/gtaxmjld/p/4443115.html
Copyright © 2011-2022 走看看