Hibernate懒加载

Hibernate懒加载

五月 01, 2019

问题

Hibernate OneToOne双向关联没有外键的一方不能Lazy加载

原因

  1. hibernate默认使用代理(Javassist实现)实现懒加载
  2. 注解为需要懒加载的关联对象会先由hibernate提供一个对应的代理,当访问该对象的属性时实际由代理响应,因此hibernate能够得知何时传来了访问请求,然后提交数据库查询获得数据值填入代理并且返回
  3. 当关联关系为ToMany时,当前实体拥有的是一个集合,存在关联对象时集合中便具有响应的元素,不存在关联对象时集合仍存在,只不过size()=0。因此需要懒加载时hibernate统一提供org.hibernate.collection下的集合用于拦截访问请求
  4. 而当关联关系为ToOne时,情况有所不同。若当前实体为关系维护方(外键所在方),查询该实体时hibernate就可以通过检查外键得知是否存在与其关联的实体,若存在则创建代理
  5. 而对于没有外键的一方,hibernate不知道它的关联对象是否为空,也就不知道是否要创建代理对象(没有关联对象时应该为null),”Hibernate cannot honor this request since it cannot know whether the association is null or not.”必须发起一次形如SELECT COUNT(1) FROM associate_table WHERE foreign_key= ${id}查询进行确认。因此hibernate选择默认强制直接EAGERLY将关联对象读取加载
  6. hibernate还有另一种手段提供懒加载的实现,即为字节码增强。通过在buildtime或runtime将代码织入实体类,可以在访问当前实体的getter、setter时就感知到并进行懒加载。
  7. 但当使用字节码增强时,具体实体类继承自@MappedSuperClass的泛型属性无法被增强,导致hibernate根本无法感知是否访问了该属性,因此也就不会触发查询,访问时始终会获得null。与此链接较为相似(但不完全相同)。字节码增强与@MappedSuperClass相关的问题比较多且不好解决(很多也不准备解决)。并且由于字节码增强与动态代理并不能共存(待验证),无法为一个项目同时使用两种方案实现懒加载。

解决方案

  • 不使用字节码增强,将关联关系改为OneToMany与ManyToOne

    1. 修改注解
    2. 修改实体数据结构
    3. 修改相应查询语句(join实体时改为复数)
    4. 修改getter、setter
  • 使用字节码增强

    1. 添加hibernate字节码增强插件,启用enableLazyInitialization= true
    2. 为需要懒加载的*ToOne一方添加注解@LazyToOne(LazyToOneOption.NO_PROXY )
    3. 修改各应用了泛型的@MappedSuperClass注解的类,将泛型属性移动至子类
  • 手动实现懒加载