是否应该在@ManyToMany关系的双方指定@JoinTable?

我有一个实体和一个实体。课程和用户之间存在多对多关系,因为一个课程可以有多个用户,并且用户可以注册多个课程。在这两个实体中,我都已将注释放在特定字段上,也就是说,在我有:CourseUser@ManyToManyCourse

@ManyToMany
private List<RegisteredUser> members;

在我有:User

@ManyToMany
private List<Course> coursesTaken;

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


答案 1

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

首先看看javadoc@JoinTable

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

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

拥有关系的字段。必需,除非关系是单向的。

对于Hibernate(5.0.9.Final)中的(双向)示例,如果只有两个注释并且没有元素,则默认值将有两个表和两个:@ManyToManymappedBy=EntityJoin 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))

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

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

添加到实体不会更改任何内容:它只是命名表的方式与命名表的方式相同。@JoinTable(name="Member_Course")Member

由于实体不再拥有其关系,因此不会创建额外的:CourseManyToManyJoinTable

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))

这对开发人员很重要,因为他或她必须了解,除非将关系添加到拥有实体(在本例中为实体)中,否则不会持久化任何关系。但是,由于这是双向关系,因此开发人员无论如何都应该同时添加 a 和 a。MemberCourseMember.coursesMemberCourse.members

因此,如果您有关系,这意味着您涉及两个实体,那么您应该在其中一个实体上添加一个,以避免冗余。由于它是双向的,我认为你制作实体的哪一方并不重要。与往常一样,启用 sql 日志并查看数据库中发生的情况始终是一个好主意:bidirectionalManyToManyManyToManymappedBy="name"join tableowning

引用:

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

关系所有者在双向关系中意味着什么?

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

防止 toString()?


答案 2

您实际上可以在两侧使用@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”和一个连接表“乐器歌手”进行寻址,则会导致数据库中出现两个不同的连接表。

这很有道理,因为从数据库的角度来看,多对多关系没有所有权的一面。拥有一方意味着拥有关系外键的一方。但是,“歌手”和“乐器”表都没有相互指代的外键。外键位于它们之间的必要联接表中

@JoinTable关系双方的优势:比方说,歌手开始学习一种新的乐器:你可以将乐器添加到歌手身上(反之亦然,因为它是双向的)并更新/合并歌手。此更新将仅更新歌手和联接表。它不会触及仪器台

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

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


推荐