JPA -使用mappedBy属性定义所属实体的差异

dzjeubhm  于 2023-10-19  发布在  其他
关注(0)|答案(5)|浏览(130)

下面两个声明究竟有什么不同
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中有等效的。

s2j5cfk0

s2j5cfk01#

JPA 2.0 specification第2.9节写道:
关系可以是双向的,也可以是单向的。双向关系既有拥有方,也有反向(非拥有)方。单向关系只有拥有的一方。关系的拥有方决定数据库中关系的更新,如3.2.4节所述。
以下规则适用于双向关系:

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

第3.2.4节的相关部分为:
持久化实体的状态在事务提交时同步到数据库。这种同步涉及将对持久化实体及其关系的任何更新写入数据库,如上面所指定的。

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

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

bybem2ql2#

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

数据库中没有任何变化,因为你修改了反向端,忘记修改拥有端。

t2a7ltrp

t2a7ltrp3#

正如其他人所指出的,你在例子中关于哪一方是拥有方的问题上是错误的。对于拥有方,我们的意思是从OO的Angular 拥有关系,在实践中,如果使用rdbm作为持久性提供者,那么它通常会与在db中生成或将生成的关系相反。
在正常情况下,OO模型很清楚地表明了哪一方是拥有方。例如,订单具有OrderLines。如果我们删除一个订单,所有的订单行都应该被删除。如果我们删除一个OrderLine,订单可能仍然有存在的权利。因此,命令是拥有方。
对于一个更具体和优秀的例子,关于哪一方是拥有方的影响,我参考了@JB Nizet的答案。
根据JPA 2.0 spec的第2.9节:
对于一对一双向关系,拥有方对应于包含相应外键的一方。
但在同一节中,我们也有:
此外,本规范还要求支持以下备选Map策略:[..]通过连接表Map实现单向和双向一对一关系、双向多对一/一对多关系以及单向多对一关系的Map。
在同一节中再往下一点,它继续说:
可以指定附加的Map注解(例如,列和表Map注解)来覆盖或进一步细化第2.10节中描述的默认Map和Map策略。一些实现利用这一点来允许一对一双函数的FK位于目标表中。
要阅读一些解决这种情况的策略,请参阅:一个几乎很好的解释
我还没有检查,但我希望并相信2.1将删除第一个报价。因为实际的数据库结构应该尽可能少地限制我们如何将数据建模为实体。

v1uwarro

v1uwarro4#

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

wgeznvg7

wgeznvg75#

您提供的示例是***一对一双向***关系。
双向关系意味着 * 两个实体将在域模型中存储彼此的引用 *。但是,

  • 这并不意味着两个表也将存储对彼此的引用。关系在数据库级别仍然是单向的。*

在双向关系中,有:

  • 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;
}

这里,假设实体AB的对应表分别是ab
我们在实体A中使用了@JoinColumn,并将其值设置为b_id。这意味着将在表a中创建一个名为b_id的列,该列将存储id或表b的引用。此列b_id也用作外键。注意,虽然是双向关系,

  • 表b未存储对表a的引用 *。

因为,表a存储关系信息,即对于表b的引用,其对应的实体A被称为 * 所有者实体 *。
另一个实体B称为引用实体。它使用mappedBy属性引用所有者实体A
类似地,在第二个示例中,B是拥有实体,A是 * 引用实体 *。

相关问题