在双向@ManyToMany中的MappedBy:原因是什么?

12
  1. 在双向多对多关系中设置MappedBy的原因是什么?
  2. 当一张表有大量记录而另一张表只有少量记录时,将mappedBy放在哪一侧更好?
2个回答

28

这实际上是一个很好的问题,并且有助于理解“拥有”实体的概念。如果您想要防止双向关系中的两个方向都有join tables,那么您需要在一侧使用mappedBy=元素,这是一个好主意。

是否有join table@ManyToMany注释的mappedBy="name"元素控制。 关于ManyToMany注释的mappedBy的Javadoc说

拥有关系的字段。除非关系是单向的,否则必填。

对于您的(双向)示例,如果只有两个@ManyToMany注释而没有mappedBy=元素,则默认情况下将有两个Entity表和两个Join Tables

Hibernate: create table SideA (id bigint not null, primary key (id))
Hibernate: create table SideA_SideB (sidea_id bigint not null, sidebs_id bigint not null, primary key (sidea_id, sidebs_id))
Hibernate: create table SideB (id bigint not null, primary key (id))
Hibernate: create table SideB_SideA (sideb_id bigint not null, sideas_id bigint not null, primary key (sideb_id, sideas_id))

虽然这句话是在说每个实体“拥有”它的ManyToMany关系,但在典型情况下,额外的join table是多余的,而且Javadoc说你需要一个mappedBy注释。如果我决定让SideA“拥有”这个关系,那么我需要在SideB实体中添加mappedBy=元素来指定它不拥有这个关系:
@Entity
public class SideA {
    @ManyToMany
    Set<SideB> sidebs;
}
@Entity
public class SideB {
    @ManyToMany(mappedBy="sidebs")
    Set<SideA> sideas;
}

由于SideB实体不再拥有它的ManyToMany关系,因此不会创建额外的JoinTable

Hibernate: create table SideA (id bigint not null, primary key (id))
Hibernate: create table SideB (id bigint not null, primary key (id))
Hibernate: create table SideA_SideB (sideas_id bigint not null, sidebs_id bigint not null, primary key (sideas_id, sidebs_id))

这对开发人员非常重要,因为他们必须明白,除非将关系添加到拥有实体(在本例中为SideA实体),否则不会保存任何关系。因此,如果您有一个双向的ManyToMany关系,这意味着涉及到两个实体的ManyToMany,那么应根据Javadoc在其中一个实体上添加mappedBy="name",以避免出现冗余的join table

关于选择哪一方作为拥有实体的一方,没有正确答案,这取决于你的系统认为什么最好。只有在将条目放入拥有方时,关系才会被持久化,因此你必须问自己,你更常更改 SideA 的列表还是 SideB 的列表。如果 SideA 拥有关系,则通过将 SideB 实例从 SideA 实例中添加或删除来更新关系,但如果你有一个 SideBSideA 实例列表需要持久化,则需要迭代列表并更改列表中每个 SideA 实例。

像往常一样,启用 SQL 日志并查看数据库中正在发生的事情总是一个好主意:

编辑:如果你有一个只创建单个联接表且没有设置 mappedBy 的持久性提供程序,则必须查看文档以确定哪一方“拥有”关系。可能是两者都不拥有,也可能是两者都拥有,更新任何一方都将持久化实体。

参考资料:

单向关联和双向关联之间有什么区别?

在双向关联中,什么是关系所有者?

ORM映射中的“拥有方”是什么意思?

避免toString()方法中无限递归的最有效方式是什么?


1
有趣的是,即使在任何一侧都没有使用mappedBy,Play! 2.6上的ebean 4.0.2仍然只创建一个表。 - Luke
仍然感到困惑,因为它只创建了一个没有mappedBy的联接表。 - Arun Raaj
使用@JoinTable注释将影响联接表的创建,本答案不涉及此问题。 - K.Nicholas

1

mappedBy将双向关系的两个方向连接起来。您需要将mappedBy放在关系的所有者上,而不是基于某个记录拥有多少(即面向对象设计)。您可以在任何JPA教程和文档中找到这些信息。


1
文档说明放置mappedBy没有区别,即我可以选择任何一方作为所有者。我不清楚为什么在多对多关系中需要mappedBy。在多对一/一对多中很清楚。我怀疑这对生产力有影响,这是我问题的第二部分原因。 - Dmitry
正如我所说,双向关系需要。1-N、1-1、M-N与此问题无关。哪一方定义所有者?请参阅JPA规范。 - Neil Stockton
4
实际上,你将 mappedBy 放在了关系的反向端。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接