JPA - mappedBy属性定义拥有关系的实体的使用差异

9
以下两个声明有何不同?
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中有相应的功能。


2
你检查过在这两种情况下A表和B表是否互相有外键关联吗? - Piotr Nowicki
5个回答

14

JPA 2.0规范第2.9节写道:

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

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

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

第3.2.4节的相关部分如下:

持久化实体的状态会在事务提交时与数据库同步。此同步包括按上述方式将对持久性实体及其关系的任何更新写入数据库。

并且

托管实体之间的双向关系将基于关系拥有方所持有的引用进行持久化。当拥有方和反向方发生更改时,开发人员有责任保持内存中引用的一致性。

在单向一对一和一对多关系的情况下,开发人员有责任确保关系的语义得到遵守。

特别重要的是要确保对关系的反面进行更改时,在所有者端进行适当的更新,以确保这些更改在同步到数据库时不会丢失。


7

拥有方是指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);

由于您修改了反向关系并忘记修改拥有方,因此数据库中不会发生任何更改。


6
正如其他人指出的那样,您在示例中错误地确定了哪一方是拥有方。 所谓拥有方是指从OO角度拥有关系,在实践中,如果使用rdbm作为持久化提供程序,则通常与将要或已经在数据库中生成的方式相反。
在正常情况下,OO模型很清楚地表明哪一方是拥有方。例如,订单具有订单行。如果我们删除一个订单,所有订单行都应该被删除。如果我们删除订单行,则订单可能仍然存在权利。因此,订单是拥有方。
对于更具体和优秀的例子,关于哪一方是拥有方的影响,我参考@JB Nizet的答案。
根据JPA 2.0规范第2.9节:
对于一对一双向关系,拥有方对应于包含相应外键的一侧。
但在同一节中,我们也有:
此外,该规范还要求支持以下替代映射策略:[..]通过联接表映射实现单向和双向一对一关系、双向多对一/一对多关系以及单向多对一关系。在同一部分的稍后位置,它继续说道:可以指定其他映射注释(例如列和表映射注释)以覆盖或进一步细化第2.10节中描述的默认映射和映射策略。一些实现利用这一点允许双向OneToOne的FK在目标表中。要了解有关解决该场景的策略的更多信息,请参见:一个几乎好的解释。我没有检查,但我希望并相信2.1将删除第一个引用。因为实际的数据库结构应该尽可能少地限制我们如何将数据建模为实体。

1
不,JPA规定一对一双向关联的拥有方是包含外键的那个。双向一对多也是如此。 - JB Nizet
“它可以用于一对一映射,其中引用实体的主键用作参考实体的外键。”http://docs.oracle.com/javaee/6/api/javax/persistence/PrimaryKeyJoinColumn.html。这种情况并不经常出现。 - esej
1
这并不改变任何事情。在这种情况下,主键列是连接列(因此称为PrimaryKeyJoinColumn),包含此注释的实体,因此连接列是拥有方。阅读@meriton的答案,其中包含JPA规范的以下摘录:“对于一对一的双向关系,拥有方对应于包含相应外键的一侧。” - JB Nizet
1
是的,我承认错误。我可以在EclipseLink中完成它,但我完全忽略了它不符合2.0最终规范。规范中的措辞让我觉得实现违反了规范。我会更新我的答案,摆脱谎言。谢谢。 - esej

5
在第一个例子中,A表将拥有2列idb_idB表将只有一列id,这使得A成为所有方。
在第二个例子中,B是所有方。 B有两列,ida_idA将只有一列id
这就是区别所在 :-)

0
你提供的例子是一个一对一双向关系。
双向关系意味着两个实体在领域模型中都存储对方的引用。但是,
这并不意味着两个表也会相互存储对方的引用。在数据库层面上,这个关系仍然是单向的。
在双向关系中有:
1. 拥有方 2. 引用方 那么,什么是拥有方呢? 让我们通过你的第一个例子来理解。
(我添加了@JoinColumnTable注解,以便初学者更清楚地理解)。
@Entity
@Table(name="a")
class A {
   @Id int id;

   @OneToOne
   @JoinColumn(name="b_id")
   B b;
}

@Entity
@Table(name= "b")
class B {
   @Id int id;

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

假设在这里,实体A和实体B的对应表分别是a和b。
我们在实体A中使用了@JoinColumn,并将其值设置为b_id。这意味着在表a中将创建一个名为b_id的列,用于存储表b的id或引用。该列b_id也充当外键。请注意,尽管存在双向关系,
“表b不存储对表a的引用”。
由于表a存储了关系信息,即对表b的引用,因此它对应的实体A被称为“拥有者实体”。
而另一个实体B被称为引用实体。它使用mappedBy属性引用拥有者实体A。
同样地,在第二个例子中,B 是拥有实体,而 A引用实体

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