(Transfer) Understanding about JAP FetchType.LAZY (hibernate implementation).

JPA defines the relationship between entities as follows:

@OneToOne @ManyToOne @OneToMany @ManyToMany

When defining them, you can specify the loading method through the fetch attribute, which has two values:

FetchType.LAZY: lazy loading FetchType.EAGER: eager loading

Eager loading is easy to understand. When loading an entity, the properties and fields that are defined to be eagerly loaded will be loaded from the database immediately . The most problematic development process is lazy loading, and the problem are all one:

"Why do I define lazy loading, but it doesn't work, and the related properties or fields are still loaded immediately?"

For this problem, my understanding is this, we first assume the following insinuation relationship:

@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(){
      ...
  }
}

 

The relationship of the tables is: the orders table is a separate table, the order_items table has a foreign key (order_id) that references the orders table, and order_finance has a foreign key (order_id) that references the orders table. order_items------->orders <------------order_finance

customer

The problem now is:
Order.lineItems The LAZY lazy loading of @OneToMany works , but the lineItems are not found when the order is
found Order.customer The LAZY lazy loading of @OneToMany is functional , and the order is not found when the order is found. Customer
Order.salePrice The LAZY lazy loading of @OneToOne does not work . After the order is found, the related OrderPrice will also be fetched.
LineItem.order The LAZY lazy loading of @ManyToOne works , and the find lineitem does not find the relevant order.
OrderPrice.order The LAZY lazy loading of @OneToOne doesn't work . When orderprice is found, the relevant order is found.

Lazy loading, as the name implies, is to load from the database when accessing specific properties. For example, in the example, the Order entity should be loaded only when OrderPrice.getOrder() is called, and Order should not be loaded when OrderPrice is loaded.

So first think about it, for lazy loading, how does hibernate know when to call the get method of the related entity?
The answer is that it doesn't know, hibernate doesn't know when the relevant get method will be called, so how does hibernate realize that it only loads when it is accessed? Hibernate uses a proxy (Proxy), and the call to the entity will be accepted and processed by the proxy. Hibernate can set the proxy to load data when it is called, thereby implementing lazy loading. Then for a mapped object, either it has a value or it is null, it is not useful to establish a proxy for null values, and it is not possible to establish a dynamic proxy for null. That is to say, hibernate should consider whether the mapped object is null when establishing a proxy for lazy loading. If it is null, there is no need to create a proxy, directly set the mapped value to null, if the mapped object is not null, then hibernate will create a proxy object

延迟加载失败都是由于确定映射的内容是否是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方法异常

There is also a case where lazy loading "seems not to work": it actually works, but the code somewhere may call the relevant get method to load the lazy loaded object, so it looks like no success

Summarize:

  • For lazy loading, hibernate cannot know when the get method of the lazy loaded property/field will be called, so for the lazy loaded property/field, hibernate will wrap (Wrapper) it by establishing a proxy Proxy
  • The proxy may be established according to the entity itself, or it can be established according to a collection. If it is established according to a collection, lazy loading can generally be successful. If it is established according to an entity, null cannot establish a proxy. If it can be determined that the proxy class must exist , then the lazy loading can be successful, and the relevant mapping is placed in the proxy class. If it is not sure whether the mapped attribute exists, it will go to the database for query, which will cause the delay to fail. The foreign key definition can let hibernate know whether the mapped property exists or not, and can also tell hibernate by optional=false that the mapped property must exist.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326957001&siteId=291194637