双向关系拥有方的想法源于这样一个事实,即在关系数据库中没有像对象那样的双向关系。
在数据库中,我们只有外键,其中只有一个表可以有另一个表的外键。让我们举一个不能按预期工作的例子,看看为什么需要 mappedBy:
@Entity
@Table(name="PERSONS")
public class Person {
@OneToMany
private List<IdDocument> idDocuments;
}
@Entity
@Table(name="IDDOCUMENT")
public class IdDocument {
@ManyToOne
private Person person;
}
这不仅会创建表 PERSONS 和 IDDOCUMENTS,还会创建第三个表 PERSONS_IDDOCUMENTS:
CREATE TABLE persons_iddocument
(
persons_id bigint NOT NULL,
iddocuments_id bigint NOT NULL,
CONSTRAINT fk_persons FOREIGN KEY (persons_id) REFERENCES persons (id),
CONSTRAINT fk_docs FOREIGN KEY (iddocuments_id) REFERENCES iddocument (id),
CONSTRAINT pk UNIQUE (iddocuments_id)
)
仅注意文档上的主键。在这种情况下,Hibernate 独立跟踪关系的双方:如果您将文档添加到关系 Person.idDocuments,它会在 PERSON_IDDOCUMENTS 中插入一条记录。
如果我们更改 IdDocument 的 Person,它会更改表 IDDOCUMENTS 上的外键 person_id。
Hibernate 是在数据库上创建两个单向(外键)关系,来实现一个双向对象关系,因为数据库不支持双向关系。
但我们想要的是对象关系仅通过表 IDDOCUMENTS 上的外键映射到 PERSON:一份文档只属于一个人。
不需要额外的表,这将迫使我们在同一个数据库事务中同时修改 Person.idDocuments 和 IdDocument.person 以保持关系一致。
为了解决这个问题,我们需要配置 Hibernate 以停止跟踪对关系 Person.idDocuments 的修改。 Hibernate 应该只跟踪关系 IdDocument.person 的另一端,为此我们添加 mappedBy:
@OneToMany(mappedBy="person")
private List<IdDocument> idDocuments;
这意味着“关系这一侧的修改已经被关系 IdDocument.person 的另一侧映射,因此无需在此处在额外的表中单独跟踪它”。
这为我们提供了我们想要的映射,但有一个主要后果:
- Hibernate 不再跟踪对 Person.idDocuments 集合的修改,开发人员有责任修改 IdDocument.person 以修改关联。