应该在@ManyToMany关系的两边指定@JoinTable吗?

时间:2016-04-22 21:06:28

标签: java hibernate jpa many-to-many entity-relationship

我有一个实体Course和一个实体User。在课程和用户之间存在多对多关系,因为课程可以包含许多用户,并且用户可以在许多课程中注册。在这两个实体中,我将@ManyToMany注释放在特定字段上,即Course我有:

@ManyToMany
private List<RegisteredUser> members;

并在User我有:

@ManyToMany
private List<Course> coursesTaken;

现在,我知道这种多对多关系通常由第三个表格表示。我也知道注释@JoinTable允许我们这样做。我不知道的是,我是否应该在两个不同实体的两个字段中添加此注释@JoinTable。顺便说一句,如果我需要添加到两者,名称需要匹配正确吗?

4 个答案:

答案 0 :(得分:11)

这实际上是一个很好的问题,它有助于理解“拥有”实体的概念,因为任何一方都不需要@JoinTable注释。如果你想阻止双方拥有join tables,这是一个好主意,那么你需要在一边有一个mappedBy=元素。 @JoinTable注释用于指定表名或映射关联的列。

首先看一下Javadoc for @JoinTable

  

指定关联的映射。它适用于协会的拥有方。

是否存在join tablemappedBy="name"注释的@ManyToMany元素控制。 Javadoc for mappedBy for the ManyToMany annotation says

  

拥有这种关系的领域。除非关系是单向的,否则是必需的。

对于Hibernate(5.0.9.Final)中的(双向)示例,如果只有两个@ManyToMany注释而没有mappedBy=元素,则默认将有两个Entity表和两个Join Tables

Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Course_Member (Course_id bigint not null, members_id bigint not null, primary key (Course_id, members_id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (Member_id bigint not null, courses_id bigint not null, primary key (Member_id, courses_id))

虽然这是说每个实体“拥有”其ManyToMany关系,但额外的join table在典型用例中是多余的。但是,如果我决定让Member实体“拥有”该关系,那么我将mappedBy=元素添加到Course实体,以指定它不拥有该关系:< / p>

@ManyToMany(mappedBy="courses")
Set<Member> members;

@JoinTable(name="Member_Course")添加到Member实体不会改变任何内容:它只是将表命名为无论如何命名。

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

Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (members_id bigint not null, courses_id bigint not null, primary key (members_id, courses_id))

这对开发人员很重要,因为他或她必须明白,除非将关系添加到拥有实体(在本例中为Member实体),否则不会保持任何关系。但是,由于这是一种双向关系,因此开发人员应该将{strong> 一个Course添加到Member.courses,将Member添加到Course.members

因此,如果您有bidirectional ManyToMany关系,这意味着您对所涉及的两个实体都有ManyToMany,那么您应该在其中一个实体上添加mappedBy="name"避免使用冗余join table。由于它是双向的,我认为你在owning实体的哪一方面并不重要。与往常一样,启用sql日志并查看数据库中发生的事情总是一个好主意:

参考文献:

What is the difference between Unidirectional and Bidirectional associations?

What does relationship owner means in bidirectional relationship?

What is the “owning side” in an ORM mapping?

Most efficient way to prevent an infinite recursion in toString()?

答案 1 :(得分:1)

不。双方都获得@ManyToMany,但只有一方拥有@JoinTable

More ManyToMany info here

答案 2 :(得分:0)

对于@ManyToMany处理现有模式(不是由Hibernate创建),您必须在两个类上使用@JoinTable注释来指定表以及哪些列映射到相应类中的Java成员变量。我认为这个例子可以帮助你将哪些属性传递给注释:

https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

答案 3 :(得分:0)

您实际上可以在两面都使用@JoinTable ,通常这很合理!在寻找这种解决方案数周之后,我的经验不足。

即使遍及整个互联网,博客和文章都讲述了一个不同的故事-而且JPA的Javadoc容易以这种方式被误解(或错误)。在看到一本针对专业人士的书中未注释的示例之后,我尝试了此方法-并成功了。

方法:

歌手-乐器协会: 歌手方

@ManyToMany 
@JoinTable(name = "singer_instrument", joinColumns =
@JoinColumn(name = "SINGER_ID"), inverseJoinColumns = @JoinColumn(name = "INSTRUMENT_ID")) 
public Set<Instrument> instruments;

与另一端完全相同! 仪器方面

@ManyToMany
@JoinTable(name = "singer_instrument",
joinColumns = @JoinColumn(name = "INSTRUMENT_ID"),
inverseJoinColumns = @JoinColumn(name = "SINGER_ID"))
public Set<Singer> singers;

因此,如果您使用相同的名称处理相同的联接表“ singer_instrument”,则可以使用。 但是,如果您处理一个连接表“ singer_instrument”和一个连接表“ instrument-singer”,则会导致数据库中出现两个不同的连接表。

这很有意义,因为从数据库角度看,多对多关系没有没有拥有一方。拥有方是指拥有关系的外键的一方。但是表“ singer”和“ instrument”都没有互相引用的外键。外键位于它们之间必要的联接表中。

@JoinTable的关系的优势: 假设某个歌手开始学习一种新乐器:您可以将乐器添加到歌手(反之亦然,因为它是双向的),并且可以更新/合并该歌手。此更新将仅更新歌手和联接表。它不会触摸仪器表

现在另一种情况-吉他课程已经结束,因此您想要删除吉他与以前的课程参与者/歌手之间的连接:从歌手删除乐器“ guitar”之后(反之亦然!) ,您可以更新/合并乐器。此更新将仅更新工具和联接表。它不会触摸歌手表

如果仅在一侧具有@JoinTable,则始终必须更新/保存/删除这一侧,以安全地处理联接表中的条目(歌手与乐器之间的关系)。在这种情况下,您必须更新结束吉他课程的每位歌手。 不能正确反映关系的类型,并可能导致性能问题数据交易期间的冲突