I amQiana , WELCOME.

NO TRY , NO HIGH

Hibernate lazy loading

@OneToMany(fetch = FetchType.LAZY, mappedBy = "employee",cascade=CascadeType.ALL)
public Set<Schedule> getSchedules() {
  return schedules;
}

DAO层代码中@OneToOne,@ManyToOne,@OneToMany,@ManyToMany只是定义了entity之间的关系,还需要通过fetch属性指定加载方式。Hibernate定义了两种加载方式:

  • FetchType.LAZY —-LAZY = fetch when needed
  • FetchType.EAGER —-EAGER = fetch immediately

如果是EAGER,那么表示取出这条数据时,它关联的数据也同时取出放入内存中
如果是LAZY,那么取出这条数据时,它关联的数据并不取出来,在同一个session中,什么时候要用,就什么时候取(再次访问数据库)。

Hibernate 默认将OneToMany 及ManyToMany设置为lazy

Hibernate N+1问题

在Hibernate 二个或者多个对象有关联的情况,并且设置fetch = FetchType.EAGER。在执行hql查询语句的时候,查询一个表,将会随便查询出所有相关联的表,
即:发出一条sql语句后,继续发出N条sql语句。
这种问题在多对一关联(最为常见)尤为严重。假如存在course和schedule表,且为OneToMany。course表存在10条数据,schedule表存在100条数据。
当我们需要查出所有的course时,查询会产生
1(select * from course)+10(select schedule … where courseId=XXX)条sql语句。

下面是摘自martin fowler对于lazy loading的解释,原文链接Â

There are four main varieties of lazy load.

  • Lazy Initialization uses a special marker value (usually null) to indicate a field isn’t loaded. Every access to the field checks the field for the marker value and if unloaded, loads it.
  • Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real object and then delegates.
  • Value Holder is an object with a getValue method. Clients call getValue to get the real object, the first call triggers the load.
  • A ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.

在前后端分离的项目中,如果设置FetchType.EAGER。需要做如下配置

  • 将@ResponseBody注解加入到controller的方法中,如下:

     @RequestMapping(value = "/employees", method = RequestMethod.GET)
    public @ResponseBody List<Employee> getEmployees() {
    
     return employeeService.getEmployees();
    }
    

当在一个遗留项目,我们之前用到了jsp,现在要前后端分离使用angular,在这种情况下,就必须使用到lazy loading。
那么hibernate 本身在向前端传数据时就会出现如下问题:

Error 500 Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?

原因就是无法对设置了lazy loading 的外键部分进行加载。
解决方法是需要工具对其进行序列化,链接here

完成上述配置后需要在dao层显示的破坏掉lazy loading

for (Customer customer : customerList) {
// if (customer.getEmployee() != null) {
//    customer.getEmployee().getEmail();
//  }
   Hibernate.initialize(customer.getEmployee());
   }

最后想说的是,Lazy Loading 这个问题并不是Hibernate 独有,事实上很多Object/Relational Mapping (ORM) 的程式库,为了要争取一些执行上的效率、降低内存空间与数据传输负荷,几乎都采用类似的作法。