Hibernate:一对一延迟加载,可选=假

81

我遇到了一个问题,即在 Hibernate 中 一对一的懒加载 不起作用。我已经解决了这个问题,但仍然不太理解发生了什么。

我的代码(此处懒加载无效,当我拉取 Person 时,Address 也被获取):

@Entity
public class Person{

  @Id
  @SequenceGenerator(name = "person_sequence", sequenceName = "sq_person")
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_sequence")
  @Column(name = "id")
  private long personID;

  @OneToOne(mappedBy="person", cascade=CascadeType.ALL, fetch = FetchType.LAZY)
  private Adress address;
  //.. getters, setters
}

@Entity
public class Address {

  @Id
  @Column(name="id", unique=true, nullable=false)
  @GeneratedValue(generator="gen")
  @GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="person"))
  private long personID;

  @PrimaryKeyJoinColumn
  @OneToOne
  private FileInfo person;
}

但是:如果我在一对一关系中添加 optional=false,懒加载正常工作

@OneToOne(mappedBy="person", cascade=CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
private Adress address;

问题/请求:请解释一下 optional=false 注解如何帮助实现延迟加载。

P.S.我已经阅读了post1post2中的文章,并了解了简单的 OneToOne 为什么不能是延迟加载,但我仍然无法理解 optional=false 的神奇之处。


嘿@Volodymyr,我和你一样有同样的问题。我试图从一个实体中分离出BLOB列。父实体有子实体。子实体包含二进制列。父亲和孩子是“同一张表”,所以我使用了@OneToOne关系。尽管我使用了LAZY fetchType,但它似乎不起作用。当我放置optional=false时,它就起作用了。任何解释都会受到真正的赞赏。 - emeraldhieu
@Emerald214 抱歉,那是两年前的事了。目前我正在编写JS移动应用程序,无法帮助你。 - VB_
OneToOne optional = false 与 CascadeType.PERSIST 不兼容,详见:https://hibernate.atlassian.net/browse/HHH-9670 - sliver
2个回答

103
如果关联是可选的,Hibernate 没有办法知道给定人员是否存在地址,因此它不能使用代理填充地址字段,因为可能没有任何引用该人员的地址;也不能将其填充为 null,因为可能存在引用该人员的地址。
当你使关联成为必需的(即 optional=false),它会相信你并假设地址存在,因为关联是必需的。因此,它直接使用代理填充地址字段,因为知道存在一个引用该人员的地址。

1
如果您在没有地址的情况下尝试保存“Personne”,则“optional=false”无法正常工作:“org.hibernate.PropertyValueException:not-null property references a null or transient value:” - Grégory
8
optional = false 表示地址不是可选的,因此是必填的。将其设置为 null 将会抛出异常,这是可以预料的。 - JB Nizet
3
除了它实际上不会懒加载关联外,这是可能的。如果使用LazyToOne(NO_PROXY)并在构建时对字节码进行检测也是可能的,但我记得我曾经有过不好的经历。如果关联是可选的,最好使用专用的连接列。 - JB Nizet
1
是的,我已经测试了LazyToOne(NO_PROXY)并在构建时通过操纵字节码来实现,但像你一样(使用另一个库HibernateJackson),我有糟糕的经历。最终我决定不映射关联,并在不同的DAO中单独管理表格。 - Grégory
1
optional = false 对我无效,它仍然急切地获取那些实体。@OneToOne(fetch = FetchType.LAZY, mappedBy = "fundSeries", optional = false) private FundSeriesDetailEntity fundSeriesDetail; - Oleg Kuts
显示剩余3条评论

13

最简单的方法是伪造一对多的关系。这个方法能够奏效,因为加载集合的惰性加载要比加载单一可空属性容易得多,但如果您使用复杂的JPQL/HQL查询,则通常这种解决方案非常不方便。

另一种方法是使用构建时字节码插装。有关更多详细信息,请阅读Hibernate文档:19.1.7。使用延迟属性获取。记住,在这种情况下,您必须向一对一关系添加@LazyToOne(LazyToOneOption.NO_PROXY)注释以使其变为懒加载。将fetch设置为LAZY是不够的。

最后一种解决方案是使用运行时字节码插装,但它仅适用于在完整的JEE环境中使用Hibernate作为JPA提供程序(在这种情况下,将"hibernate.ejb.use_class_enhancer"设置为true应该管用:实体管理器配置),或者使用已配置为执行运行时编织的Spring Hibernate(在某些较旧的应用程序服务器上可能难以实现)。在这种情况下,也需要使用@LazyToOne(LazyToOneOption.NO_PROXY)注释。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接