拥有方究竟是什么意思?一些映射示例(一对多、一对一、多对一)的解释是什么?
以下文本摘自 Java EE 6 文档中对 @OneToOne 的描述。您可以在其中看到概念拥有的一面。
定义与另一个具有一对一多重性的实体的单值关联。通常不需要明确指定关联的目标实体,因为它通常可以从被引用的对象的类型中推断出来。如果关系是双向的,非拥有方必须使用 OneToOne 注解的 mappedBy 元素来指定拥有方的关系字段或属性。
为什么拥有方的概念是必要的:
双向关系拥有方的想法来自这样一个事实,即在关系数据库中没有像对象那样的双向关系。在数据库中,我们只有单向关系——外键。
“拥有方”这个名字的原因是什么?
Hibernate 跟踪的关系的拥有方是拥有数据库中外键的关系的一方。
拥有方的概念解决了什么问题?
以没有声明拥有方的两个实体映射为例:
@Entity
@Table(name="PERSONS")
public class Person {
@OneToMany
private List<IdDocument> idDocuments;
}
@Entity
@Table(name="ID_DOCUMENTS")
public class IdDocument {
@ManyToOne
private Person person;
}
从 OO 的角度来看,这个映射定义的不是一个双向关系,而是两个独立的单向关系。
该映射不仅会创建表 PERSONS
和 ID_DOCUMENTS
,还会创建第三个关联表 PERSONS_ID_DOCUMENTS
:
CREATE TABLE PERSONS_ID_DOCUMENTS
(
persons_id bigint NOT NULL,
id_documents_id bigint NOT NULL,
CONSTRAINT fk_persons FOREIGN KEY (persons_id) REFERENCES persons (id),
CONSTRAINT fk_docs FOREIGN KEY (id_documents_id) REFERENCES id_documents (id),
CONSTRAINT pk UNIQUE (id_documents_id)
)
仅注意 ID_DOCUMENTS
上的主键 pk
。在这种情况下,Hibernate 独立跟踪关系的两侧:如果您将文档添加到关系 Person.idDocuments
,它会在关联表 PERSON_ID_DOCUMENTS
中插入一条记录。
另一方面,如果我们调用 idDocument.setPerson(person)
,我们会更改表 ID_DOCUMENTS
上的外键 person_id。 Hibernate 在数据库上创建两个单向(外键)关系,实现一个双向对象关系。
拥有方的概念如何解决问题:
很多时候,我们想要的只是表 ID_DOCUMENTS
上指向 PERSONS
的外键,而不是额外的关联表。
为了解决这个问题,我们需要配置 Hibernate 以停止跟踪关系 Person.idDocuments
上的修改。 Hibernate 应该只跟踪关系 IdDocument.person
的 other 端,为此我们添加 mappedBy:
@OneToMany(mappedBy="person")
private List<IdDocument> idDocuments;
mappedBy 是什么意思?
这意味着类似:“关系这一侧的修改已经被关系 IdDocument.person 的另一侧映射,因此无需在额外的表中单独跟踪它。”
有任何问题,后果吗?
使用 mappedBy,如果我们只调用 person.getDocuments().add(document)
,ID_DOCUMENTS
中的外键将不链接到新文档,因为这不是拥有/跟踪的一面的关系!
要将文档链接到新人员,您需要显式调用 document.setPerson(person)
,因为这是关系的拥有方。
使用 mappedBy 时,开发人员有责任了解拥有方是什么,并更新关系的正确方以触发新关系在数据库中的持久性。
您可以想象拥有方是具有对另一方的引用的实体。在你的摘录中,你有一对一的关系。由于它是对称关系,因此如果对象 A 与对象 B 相关,那么您最终会得到这样的结果,反之亦然。
这意味着在对象 A 中保存对对象 B 的引用并在对象 B 中保存对对象 A 的引用将是多余的:这就是为什么您选择哪个对象“拥有”另一个引用它的对象。
当您拥有一对多关系时,与“多”部分相关的对象将是拥有方,否则您将不得不存储从单个对象到多个对象的许多引用。为了避免这种情况,第二类中的每个对象都将有一个指向它们所引用的单个对象的指针(因此它们是拥有方)。
对于多对多关系,因为无论如何您都需要一个单独的映射表,所以不会有任何拥有方。
总之,拥有方是引用另一方的实体。
@ManyToMany
关系也有拥有方。同样,@OneToMany
关系可以使用连接表,您仍然必须指定拥有方。
我将非常简要地解释这一点。 “拥有”意味着本身带有外键列。换句话说,它拥有这种关系。许多人误解了拥有这个词。他们认为拥有方是主要的一方。但是当我们看它时,带有外键列的表是链接的一面。例如:让我们考虑一下 Person 和 Adress 以及它们之间的关系 OneToOne
@Data
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "adress_id")
private Adress adress;
}
@Data
@Entity
public class Adress {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "adress")
private Person person;
}
在这种情况下, Person 具有 adress_id fk 列,该列与主键列的 Adress 链接。
一般来说,您可能会想为什么我需要拥有方?要理解这一点,您需要了解数据库规则。例如,假设您不想在一个表中使用拥有方和所有数据存储,这意味着实施糟糕的数据库实践。所以我假设你现在明白每个实体都应该将自己的数据存储在自己的表中。现在他们要求您在视图部分显示此人及其地址。如果没有拥有方,你会怎么做?您可能必须为每个实体创建 2 个查询,对吧?在这里,你也做了一个糟糕的 DML。您可以使用 JOIN
编写 1 个查询,而不是编写 2 个查询。 JOIN 与我们定义的拥有方一起工作,没有它们,我们无法从另一个表中选择属于一个表的数据。 ORM 还为您提供了一种更舒适的方式,即通过实体使用基本的 java 代码而不使用数据库。而已。
双向关系必须遵循这些规则。
双向关系的反面必须通过使用 @OneToOne、@OneToMany 或 @ManyToMany 注释的 mappedBy 元素引用其拥有方。 mappedBy 元素指定实体中作为关系所有者的属性或字段。
多对一双向关系的多方不得定义 mappedBy 元素。 多方始终是关系的拥有方。(根据 Oracle 文档:https://docs.oracle.com/cd/E19798-01/821-1841/bnbqi/index.html)
对于一对一的双向关系,拥有方对应于包含相应外键的一方。
对于多对多双向关系,任何一方都可能是拥有方。
嗯,这有很大帮助。我赞成这个讨论
特别是我在医院管理系统中寻找以下用例。 Patient->Patient History 1.Patient 不依赖于患者的病史,即只有在 Patient 访问医院时,才需要添加他的病史。 2. 在随后的访问中,历史记录被添加到历史记录表中,但需要对 Patient 的 patient_id 引用。所以这里的外键在 PatientHIstory 表中,拥有方是 PatientHistory
所以这个关系必须在患者实体中建模为双向 OneToMany,mappedby="患者"。每个实体相互引用。
不定期副业成功案例分享
person.getDocuments().add(document)
”,hibernate 会更新ID_DOCUMENTS
中的外键。@OneToMany
注释上有级联属性,可以设置为 PERSIST 在这种情况下,休眠会将所有链接的实体保存到数据库。任何人都可以澄清这一点 - 为什么作者说休眠不会跟踪非拥有方的更改 - 但实际上休眠执行跟踪?