JPA – 使用mappedBy属性来定义拥有实体的不同之处

以下两个声明的区别究竟是什么?

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中有相同的东西。

JPA 2.0规范第2.9节写道:

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

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

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

3.2.4节的相关部分是:

持久化实体的状态在事务提交时与数据库同步。 此同步涉及向数据库写入对上面指定的持久性实体及其关系的任何更新。

管理实体之间的双向关系将基于关系所属方所持有的引用而持久化。 开发人员有责任保持内存引用保持在拥有方,而保持在反方的引用在更改时保持一致。 在单向一对一和一对多关系的情况下,开发人员有责任确保遵守关系的语义。

特别重要的是要确保对关系的反面的更改导致在所有方面进行适当的更新,以确保在它们与数据库同步时不会丢失更改。

正如其他人所指出的那样,你的例子中哪一方是你自己的一方是错的。 对于拥有方而言,我们的意思是从OO角度拥有关系,在实践中,如果使用rdbm作为持久性提供程序,那么通常最终会与数据库中的生成方式或生成方式相反。

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

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

根据JPA 2.0规范第2.9节:

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

但在同一部分我们也有:

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

在同一部分中继续下去:

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

要阅读一些解决这种情况的策略,请参阅: 一个几乎很好的解释

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

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

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

这就是区别:-)

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

数据库中没有任何内容会被更改,因为您修改了反面但忘了修改拥有方。