【问题标题】:JPA One-to-One relation creates a non unique foreign keyJPA 一对一关系创建非唯一外键
【发布时间】:2020-06-09 07:22:13
【问题描述】:

我有以下实体:

@Entity
public class A {

  // id, etc..

  @OneToOne
  private B b;
}

a 已经存在,当我向其中添加新字段b 时,hibernate 执行以下操作:

alter table a add column b_id int8
alter table a add constraint FKg76mxqt8whi8t8p4i7el95910 foreign key (b_id) references b

如您所见,外键列b_id 不是唯一的。为什么会这样?一对一关系是否意味着外键必须是唯一的?这也是我在 JPA 规范中发现的单向一对一关系:

[...] 外键列与主键类型相同 表 B 上存在唯一键约束。

为了使它工作,我必须明确地将@JoinColumn(unique=true) 注释添加到该字段。为什么我必须明确地这样做?

【问题讨论】:

  • 我认为默认情况下它没有将外键设置为唯一是因为您可以在表中使用该外键拥有多行,通常很明显,并且文档没有说该外键约束默认是唯一的:javaee.github.io/javaee-spec/javadocs/javax/persistence/…
  • 但是我不应该能够在一对一关系中的多行中拥有相同的外键。一对一意味着外键只是一行的一部分。如果它包含在多行中,则它不是一对一的关系。
  • 你使用的是哪个休眠版本?
  • 不知道B模型有没有mappedBy属性。但为了拥有唯一约束,您需要让关系的非所有者方拥有mappedBy 属性集。没有它,您将建模两种不同的关系而不是一种双向关系;因此,没有什么能阻止当前模型让两个 Childs 指向同一个 Parent。请同时包含模型 B 关系的代码
  • 我有一个单向映射。所以B 没有字段A

标签: java spring hibernate jpa


【解决方案1】:

双向@OneToOne

为了创建唯一约束,您必须创建一个完整的双向OneToOne 关系。

这意味着你必须在ParentOwning)实体上添加@OneToOne注解,并在Child上添加@OneToOne(mappedBy="...")实体。

这将在您的 id 列上创建一个唯一约束。

否则,您将建模两种不同的关系,而不是一种双向关系;因此,没有什么能阻止当前模型让两个 Childs 指向同一个 Parent。

官方JavaDoc for @OneToOne annotation有更多关于附加参数的信息和双向关系的建议。

UPD:Link to hibernate specification 关于它如何处理@OneToOne 关系:

  1. 当使用 双向@OneToOne 关联时,Hibernate 在获取子端时强制执行唯一约束。
  2. 单向关联遵循关系数据库外键语义,客户端拥有关系。

你的情况

这意味着在您的 B 实体模型上,您应该使用您的 A 实体添加一个字段并使用 @OneToOne(mappedBy="b") 对其进行注释,以使您的关系双向且完整,限制对单个父项的访问并创建一个唯一的约束。

【讨论】:

  • 为什么“没有什么能阻止当前模型让两个 Childs 指向同一个 Parent”?这显然是一对一的,这与您的说法相矛盾。
  • @Andronicus 你可以在这里阅读更多内容:docs.jboss.org/hibernate/orm/current/userguide/html_single/…。正如规范中所述:When using a bidirectional @OneToOne association, Hibernate enforces the unique constraint upon fetching the child-side. 因此,当您在这里只有单向关系时,它会说:A unidirectional association follows the relational database foreign key semantics, the client-side owning the relationship. - 这可能是您的约束不唯一的原因
  • @M.Dietz 如果您发现这回答了您的问题,请将其标记为已接受的答案,谢谢
【解决方案2】:

我最近遇到了这个问题。我的假设是@OneToOne 必须在外键上添加唯一约束。但是hibernate不会那样做。

请参阅休眠文档中的 section。单向和双向映射都没有添加唯一约束(从文档中的查询可以看出)。这意味着当添加具有相同外键的行时,hibernate 不会抛出异常。

虽然请注意,在双向映射的情况下,hibernate 在获取父实体(而不是子实体)时会进行唯一性检查。如果有多个子 (owning) 实体引用了正在获取的同一个父实体,hibernate 会抛出一个org.hibernate.exception.ConstraintViolationException

解决方案是为您的孩子添加@JoinColumn(unique = true, ...)。当在表中插入具有非唯一外键的行时,这也会引发错误。

【讨论】:

  • 这是不正确的,hibernate 对双向一对一关系强制执行唯一约束,如果注释正确编写且父、子和mappedBy 设置正确,则会引发异常
猜你喜欢
  • 2021-04-10
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多