ORM 映射中的“拥有方”是啥?

Posted

技术标签:

【中文标题】ORM 映射中的“拥有方”是啥?【英文标题】:What is the "owning side" in an ORM mapping?ORM 映射中的“拥有方”是什么? 【发布时间】:2010-05-01 11:11:47 【问题描述】:

拥有方究竟是什么意思?一些映射示例(一对多、一对一、多对一)有何解释?

以下文本摘自 Java EE 6 文档中 @OneToOne 的描述。您可以在其中看到拥有方的概念。

定义一个单值关联到 另一个具有一对一的实体 多样性。这不是正常的 需要指定相关的 明确地定位实体,因为它可以 通常从类型推断 被引用的对象。如果 关系是双向的, 非拥有方必须使用 mappedBy OneToOne 注释的元素 指定关系字段或 拥有方的财产。

【问题讨论】:

***.com/questions/12493865/… 我迷路了,直到我读到这个:javacodegeeks.com/2013/04/… 具有外键列的数据库表被视为拥有方。因此,代表该数据库表的业务实体是该关系的所有者(拥有方)。不一定,但大多数情况下,拥有方都会有@JoinColumn 注释。 【参考方案1】:

为什么需要拥有方的概念:

双向关系拥有方的想法来自这样一个事实,即在关系数据库中没有像对象那样的双向关系。在数据库中,我们只有单向关系——外键。

“拥有方”这个名称的原因是什么?

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 的角度来看,此映射定义的不是一个双向关系,而是 两个单独的单向关系。

映射不仅会创建表PERSONSID_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.personother 端,为此我们添加 ma​​ppedBy

@OneToMany(mappedBy="person")
private List<IdDocument>  idDocuments;

ma​​ppedBy 是什么意思?

这意味着类似:“关系这一侧的修改已经映射为 关系 IdDocument.person 的另一端,所以不需要 在一个额外的表中单独跟踪它。”

是否有任何 GOTCHA、后果?

使用ma​​ppedBy,如果我们只调用person.getDocuments().add(document)ID_DOCUMENTS中的外键将链接到新文档,因为这不是拥有/跟踪关系的一方!

要将文档链接到新人,您需要显式调用document.setPerson(person),因为这是关系的拥有方

在使用ma​​ppedBy时,开发者有责任知道什么是拥有方,并更新关系的正确方以触发新关系在数据库中的持久化.

【讨论】:

我找到的解释“mappedBy”+“inversedBy”学说的最佳答案。 感谢您指定映射和概念背后的原因。 我不知道事情是否发生了变化,但在 Hibernate 5.0.9.Final 如果我“只调用 person.getDocuments().add(document)”,hibernate 会更新 ID_DOCUMENTS 中的外键。 cascade 告诉提供者保存子实体,即使父实体不拥有它们,因此它有效地修改了规则。如果您有(或有) mappedBy=child.field 并且没有级联,则不会保存列表的子级。此外,如果您没有 mappedBy 并且没有级联,则父级拥有该关系,如果您将新子级放入列表中然后保存父级,它将引发异常,因为新的子级 ID 不可用保存在连接表中。希望澄清事情...... :) 为了进一步澄清 CASCADE,它是关于父/子关系而不是所有者/拥有。所以它与所有权无关。所有权决定了如何在数据库中定义/存储关系,换句话说,在哪个表和列中。另一方面,父/子决定了一个动作(即持久化、删除)应该如何传播到相关实体。例如,对于 Order.LineItems 属性上具有 CASCADE=REMOVE 的 Order - LineItem 双向关系,当您删除 Order 时,LineItem(即所有者)将由于父级-> 子级联而被删除。跨度> 【参考方案2】:

您可以想象拥有方是具有对另一方的引用的实体。在你的摘录中,你有一对一的关系。由于它是一个对称关系,如果对象 A 与对象 B 相关,那么你最终会得到这样的结果,反之亦然。

这意味着在对象 A 中保存对对象 B 的引用,在对象 B 中保存对对象 A 的引用将是多余的:这就是为什么您选择哪个对象“拥有”另一个引用它的对象。

当您拥有一对多关系时,与“多”部分相关的对象将是拥有方,否则您将不得不存储从单个对象到多个对象的许多引用。为了避免这种情况,第二类中的每个对象都将有一个指向它们所引用的单个对象的指针(因此它们是拥有方)。

对于多对多关系,因为无论如何您都需要一个单独的映射表,所以不会有任何拥有方。

总之,拥有方是具有对另一方的引用的实体。

【讨论】:

感谢您的澄清。 它可能有助于查看下面我对名称“mappedBy”和“拥有方”的原因的回答,如果我们不定义拥有方会发生什么,GOTCHA,希望它有所帮助 嗯,我猜答案大部分是正确的。但至少对于 Hibernate 而言,即使是多对多关系也有拥有的一面。例如,这对更新行为有影响。仔细查看本教程的第 4 部分(“更新 Hibernate 模型类”):viralpatel.net/blogs/… 这个答案让人困惑多于帮助。当在双向关系中,两个 Entity 对象将相互引用时,说“您可以想象拥有方是具有对另一方的引用的实体”有什么好处?此外,“对于多对多关系,因为无论如何您都需要一个单独的映射表,所以不会有任何拥有方”是完全不正确的:@ManyToMany 关系也有拥有方。同样,@OneToMany 关系可以使用连接表,但您仍然必须指定拥有方。 基本上,这是一个可爱的、感觉良好的答案,因为它比事实更容易理解。【参考方案3】:

我将非常简要地解释这一点。 “拥有”意味着本身带有外键列。换句话说,它拥有这种关系。许多人误解了拥有这个词。他们认为拥有方是主要的一方。但是当我们看它时,带有外键列的表是链接的一面。例如:让我们考虑一下 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 列,该列与主键列的地址链接。

【讨论】:

【参考方案4】:

双向关系必须遵循这些规则。

双向关系的反面必须通过使用 @OneToOne、@OneToMany 或 @ManyToMany 注释的 mappedBy 元素来引用其拥有方。 mappedBy 元素指定实体中作为关系所有者的属性或字段。

多对一双向关系的多方不得定义 mappedBy 元素。 多方始终是关系的拥有方。(根据 Oracle 文档:https://docs.oracle.com/cd/E19798-01/821-1841/bnbqi/index.html

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

对于多对多双向关系,任何一方都可能是拥有方。

【讨论】:

【参考方案5】:

嗯,这很有帮助。我赞成这个讨论

特别是我在医院管理系统中寻找以下用例。 患者->病史 1. 患者不依赖于病史,即只有在患者就诊时,才需要添加他的病史。 2. 在随后的访问中,历史记录被添加到历史记录表中,但需要对 Patient 的 patient_id 引用。所以这里因为外键在 PatientHIstory 表中,所以拥有方是 PatientHistory

所以这种关系必须在患者实体中建模为双向 OneToMany,mappedby="patient"。每个实体都相互引用。

【讨论】:

以上是关于ORM 映射中的“拥有方”是啥?的主要内容,如果未能解决你的问题,请参考以下文章

三大框架的底层是啥 反射? 反射和映射的区别是啥 我是学JAVA的

这两个 Doctrine 映射的真正区别是啥?

ORM解决的主要问题是啥

对象关系映射(ORM)

对象关系映射(ORM)

Java实现关系型数据库工具类JdbcUtils系列五:ORM对象关系映射