【问题标题】:JPA - difference in the use of the mappedBy property to define the owning entityJPA - 使用 mappedBy 属性定义拥有实体的区别
【发布时间】:2012-06-13 16:12:21
【问题描述】:

下面两个声明到底有什么区别

B是拥有方

@Entity
class A {
   @Id int id;

   @OneToOne
   B b;
}

@Entity
class B {
   @Id int id;

   @OneToOne(mappedBy="b")
   A a;
}

A 是拥有方

@Entity
class A {
   @Id int id;

   @OneToOne(mappedBy="a")
   B b;
}

@Entity
class B {
   @Id int id;

   @OneToOne
   A a;
}

在“普通 SQL”中考虑这一点,我认为这与拥有两个表相同,每个表都有另一个表的外键。我不明白的是,指定哪个实体是拥有方的效果是什么,即使用“mappedBy”属性。这实际上实现了什么,因为我不相信普通 SQL 中存在等价物。

【问题讨论】:

  • 您是否检查过 A 表和 B 表在这两种情况下都相互具有 FK?

标签: java hibernate jpa persistence jpql


【解决方案1】:

正如其他人指出的那样,您在示例中哪一方是拥有方是错误的。 拥有方是指从 OO 角度拥有关系,在实践中,如果使用 rdbm 作为持久性提供程序,通常最终会与在数据库中生成或将生成的关系相反。

在正常情况下,OO 模型非常清楚哪些方是拥有方。 例如,订单有 OrderLines。如果我们删除一个订单,所有的订单行都应该被删除。如果我们删除 OrderLine,则 Order 可能仍然具有存在权。 因此,订单是拥有方。

对于一个更具体和优秀的例子,关于哪一方是拥有方的影响,我参考@JB Nizet 的回答。

根据JPA 2.0 spec 的第 2.9 节:

对于一对一的双向关系,拥有方 对应于包含对应外键的那一侧。

但在同一部分我们也有:

此外,该规范还要求支持 以下替代映射策略:[..] 单向和双向的一对一关系, 双向多对一/一对多关系,以及 通过连接表实现单向多对一关系 映射。

在它继续的同一部分中再往下一点:

其他映射注释(例如,列和表映射 注释)可以被指定覆盖或进一步细化 2.10 节中描述的默认映射和映射策略。 一些实现利用它来允许双向 OneToOne 的 FK 位于目标表中。

要了解解决该场景的一些策略,请参阅:An almost good explaination

我还没有检查,但我确实希望并相信 2.1 会删除第一个引号。因为实际的数据库结构应该尽可能少地限制我们如何将数据建模为实体。

【讨论】:

  • 否,JPA 指定 OneToOne 双向关联的拥有方是包含外键的一方。双向 OneToMany 相同。
  • "并且它可以在 OneToOne 映射中使用,其中引用实体的主键用作被引用实体的外键。" docs.oracle.com/javaee/6/api/javax/persistence/… 。这种情况只是不经常出现。
  • 这并没有改变任何东西。在这种情况下,主键列是连接列(因此名称为 PrimaryKeyJoinColumn),包含此注释的实体(因此连接列)是拥有方。阅读 @meriton 的答案,其中包含 JPA 规范的摘录:“对于一对一的双向关系,拥有方对应于包含相应外键的一方。”
  • 是的。我站得更正了。我可以在 EclipseLink 中做到这一点,但我完全错过了它不是根据 2.0 最终规范。规范中的措辞让我觉得实现正在破坏规范。我会更新我的答案以摆脱谎言。谢谢。
【解决方案2】:

JPA 2.0 specification,第 2.9 节写道:

关系可以是双向的,也可以是单向的。双向关系既有拥有方,也有反向(非拥有)方。单向关系只有拥有方。关系的拥有方决定数据库中关系的更新,如第 3.2.4 节所述。

以下规则适用于双向关系:

  • 双向关系的反面必须通过使用OneToOneOneToManyManyToMany 注释的mappedBy 元素来引用其拥有方。 mappedBy 元素指定实体中作为关系所有者的属性或字段。
  • 一对多/多对一双向关系的多方必须是拥有方,因此不能在ManyToOne 注释上指定mappedBy 元素。
  • 对于一对一的双向关系,拥有方对应于包含相应外键的一方。
  • 对于多对多双向关系,任何一方都可能是拥有方。

第 3.2.4 节的相关部分是:

持久实体的状态在事务提交时同步到数据库。这种同步涉及将任何对持久实体及其关系的更新写入数据库,如上所述。

托管实体之间的双向关系将根据关系拥有方持有的引用进行持久化。开发人员有责任在内存引用发生变化时保持拥有侧的内存引用和反向侧的引用保持一致。 在单向一对一和一对多关系的情况下,开发人员有责任确保遵守关系的语义。

特别重要的是要确保对关系反向端的更改会导致拥有端的适当更新,以确保更改同步到数据库时不会丢失。

【讨论】:

    【解决方案3】:

    拥有方是 JPA 认为知道关联是否存在的一方。假设您使用第一个示例。拥有方是没有 mappedBy 属性的一方。因此,拥有方是 A,而不是 B。

    这意味着如果你在数据库中有一个 A 和一个 B,并且你有

    A a = em.find(A.class, aId);
    B b = em.find(B.class, bId);
    a.setB(b);
    

    JPA 将保存关联(即它将 B 的 ID 存储在表 A 的连接列中)。

    如果你这样做了

    A a = em.find(A.class, aId);
    B b = em.find(B.class, bId);
    b.setA(a);
    

    数据库中什么都不会改变,因为你修改了反面而忘记修改拥有方。

    【讨论】:

      【解决方案4】:

      在第一个示例中,A 表将有 2 列 idb_idB 表将有一个列 id。这使得 A 成为拥有方。

      在第二个示例中,B 是拥有方。 B 有两列,ida_idA 将有一列,id

      这就是区别:-)

      【讨论】:

        猜你喜欢
        • 2017-04-13
        • 2022-01-13
        • 2012-07-28
        • 1970-01-01
        • 2014-02-25
        • 2010-10-31
        • 1970-01-01
        • 2017-11-13
        • 1970-01-01
        相关资源
        最近更新 更多