ORM映射中的"拥有方"是什么?

Jus*_*ner 117 java mapping orm hibernate jpa

拥有方到底意味着什么?一些映射示例(一对多,一对一,多对一)的解释是什么?

以下文本摘自Java EE 6文档中对@OneToOne的描述.你可以看到这个概念拥有方在里面.

定义与具有一对一多重性的另一个实体的单值关联.通常不必明确指定关联的目标实体,因为它通常可以从被引用的对象的类型推断出来.如果关系是双向的,则非拥有方必须使用OneToOne批注的mappedBy元素来指定拥有方的关系字段或属性.

Ang*_*ity 170

为什么必须拥有一个拥有方的概念:

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

名称"拥有方"的原因是什么?

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;
}
Run Code Online (Sandbox Code Playgroud)

从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)
)
Run Code Online (Sandbox Code Playgroud)

注意主键pkID_DOCUMENTS唯一的.在这种情况下,Hibernate独立地跟踪关系的两端:如果将文档添加到关系Person.idDocuments,它会在关联表中插入记录PERSON_ID_DOCUMENTS.

另一方面,如果我们调用idDocument.setPerson(person),我们在表上更改外键person_id ID_DOCUMENTS.Hibernate 在数据库上创建了两个单向(外键)关系,以实现一个双向对象关系.

拥有方的概念如何解决问题:

很多时候,我们要的是上表只是一个外键ID_DOCUMENTSPERSONS和额外的关联表.

为了解决这个问题,我们需要配置Hibernate来停止跟踪关系的修改Person.idDocuments.Hibernate应该只跟踪关系的另一IdDocument.person,为此我们添加mappedBy:

@OneToMany(mappedBy="person")
private List<IdDocument>  idDocuments;
Run Code Online (Sandbox Code Playgroud)

它是什么意思mappedBy?

这意味着:"关系的这一侧的修改已经 被关系IdDocument.person的另一侧映射,所以不需要在额外的表中单独跟踪它."

是否有任何GOTCHA,后果?

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

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

使用mappedBy时,开发人员有责任了解拥有方是什么,并更新关系的正确方面,以便触发数据库中新关系的持久性.

  • 我找到的最佳答案解释了教义'mappedBy'+'inversedBy'. (16认同)
  • 方式比接受的答案更好,谢谢 (5认同)
  • @Karl Nicholas,嗨,同意你的观点,我们在“@OneToMany”注释上有级联属性,可以将其设置为 PERSIST,在这种情况下,hibernate 会将所有链接的实体保存到数据库。任何人都可以澄清这一点 - 为什么作者说休眠不会跟踪非拥有方的更改 - 但事实上休眠执行跟踪? (3认同)
  • 我不知道事情是否发生了变化,但在 Hibernate 5.0.9.Final 上,如果我“只调用 `person.getDocuments().add(document)`”,hibernate 会更新 `ID_DOCUMENTS` 中的外键。 (2认同)
  • 级联告诉提供者保存子实体,即使父实体不拥有它们,也可以有效地修改规则。如果您拥有(或拥有)mappedBy = child.field并且没有级联,则列表的子项将不会被保存。另外,如果您没有mapBy并且没有级联,那么父级拥有该关系,并且如果将新子级放在列表中然后保存父级,它将抛出异常,因为新的子级ID无法使用保存在联接表中。希望可以澄清事情... :) (2认同)
  • 为了进一步澄清 CASCADE,它是关于父/子关系而不是所有者/拥有的。所以它与所有权无关。所有权决定了如何在数据库中定义/存储关系,换句话说,在哪个表和列中。另一方面,父/子确定操作(即持久、删除)应如何传播到相关实体。因此,例如,对于 Order - LineItem 双向关系,在 Order.LineItems 属性上使用 CASCADE=REMOVE,当您删除 Order 时,LineItem(即所有者)将由于父级 -&gt; 子级联被删除。 (2认同)

Jac*_*ack 135

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

这意味着保存到对象A中对对象B的引用并保存在对象B中对对象A的引用将是多余的:这就是为什么您选择哪个对象"拥有"另一个对象具有对它的引用.

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

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

总之,拥有方是指对方的实体.

  • 这个答案比它有所帮助更让人困惑.有什么好处说"你可以想象拥有方是引用另一方的实体"在双向关系中,两个实体对象都会互相引用?此外,"对于多对多的关系,因为你需要一个单独的映射表,无论如何都没有任何拥有的一面",这简直是错误的:`@ ManyToMany`关系也有自己的一面.类似地,`@ OneToMany`关系可以使用连接表,您仍然必须指定拥有方. (9认同)
  • 谢谢你的澄清. (6认同)
  • 嗯,答案大多是正确的我猜.但至少对于Hibernate来说,即使是多对多的关系也有自己的一面.例如,这会对更新行为产生影响.仔细阅读本教程的第4节("更新Hibernate模型类"):http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/ (5认同)
  • 基本上,这是一个可爱,感觉良好的答案,因为它比真相更容易理解. (4认同)
  • 对于名称'mappedBy'和'拥有方'的原因,我可以看到下面的答案,如果我们没有定义一个拥有方,GOTCHAs会发生什么,希望它有所帮助 (2认同)

sal*_*asi 7

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

}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Person 有 adress_id fk 列,该列链接到主键列的地址。

一般来说,您可能会想为什么我需要拥有方?要理解这一点,您需要了解数据库规则。例如,假设您不想使用拥有方并将所有数据存储在一个表中,这意味着实施糟糕的数据库实践。所以我假设您现在了解每个实体应该将自己的数据存储在自己的表中。现在他们要求您在视图部分显示此人及其地址。如果没有拥有方,你会怎么做?您可能必须为每个实体创建 2 个查询,对吧?在这里,您也犯了一个错误的 DML。您可以使用 编写 1 个查询,而不是编写 2 个查询JOIN。JOIN 与我们定义的拥有方一起工作,没有它们,我们无法从另一表中选择属于一个表的数据。ORM 还为您提供了一种更舒适的方式,通过实体使用基本的 java 代码,而无需使用数据库。就是这样。