我有一个实体Course
和一个实体User
。课程和用户之间存在多对多关系,因为一个课程可以有多个用户,一个用户可以注册多个课程。在这两个实体中,我都@ManyToMany
注释放在特定字段上,也就是说,在我拥有Course
@ManyToMany
private List<RegisteredUser> members;
在User
中,我有:
@ManyToMany
private List<Course> coursesTaken;
现在,我知道这种多对多关系通常由第三个表表示。我也知道有注释@JoinTable
允许我们这样做。我不知道的是,我是否应该在两个不同实体的两个字段上@JoinTable
添加此注释。顺便说一句,如果我需要添加到两者中,名称需要匹配吗?
这实际上是一个很好的问题,它有助于理解"拥有"实体的概念,因为双方都不需要@JoinTable
注释。如果你想防止双方都有 join tables
,一个好主意,那么你需要在一侧有一个mappedBy=
元素。@JoinTable
批注用于指定表名或映射关联的列。
首先看看Javadoc@JoinTable:
指定关联的映射。它适用于协会的拥有方。
是否存在join table
由@ManyToMany
注释的mappedBy="name"
元素控制。用于ManyToMany
注释的 mappedBy 的 Javadoc 说:
拥有关系的字段。除非关系是单向的,否则是必需的。
对于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
实体以指定它不拥有该关系:
@ManyToMany(mappedBy="courses")
Set<Member> members;
向 Member
实体添加 @JoinTable(name="Member_Course")
不会更改任何内容:它只是以
由于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
实体),否则不会保留任何关系。但是,由于这是一种双向关系,因此开发人员无论如何都应该向Member.courses
添加一个Course
,并为Course.members
添加一个Member
。
因此,如果您有bidirectional
ManyToMany
关系,这意味着您对所涉及的两个实体都有ManyToMany
,那么您应该在其中之一上添加一个mappedBy="name"
以避免出现冗余join table
。由于它是双向的,我认为你把owning
实体的哪一边并不重要。与往常一样,启用 sql 日志并查看数据库中发生的情况始终是一个好主意:
引用:
单向关联和双向关联有什么区别?
关系所有者在双向关系中是什么意思?
ORM映射中的"拥有方"是什么?
防止 toString() 中无限递归的最有效方法?
您实际上可以在两侧使用@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",它将在数据库中生成两个不同的连接表。
这很有意义,因为从数据库的角度来看,多对多关系没有拥有方。拥有方是指拥有关系外键的一方。但是表"歌手"和"乐器"都没有相互引用的外键。外键位于它们之间的必要连接表中。
关系双方@JoinTable的优势:假设一个歌手开始学习一种新的乐器:您可以将乐器添加到歌手(反之亦然,因为它是双向的)并更新/合并歌手。更新将仅更新歌手和联接表。它不会碰到仪器表。
现在另一种情况 - 吉他课程已经结束,所以你想删除吉他和以前的课程参与者/歌手之间的联系:从歌手中删除乐器"吉他"后(反之亦然!),你更新/合并乐器。更新将仅更新仪器和连接表。它不会碰到歌手桌。
如果只有一侧@JoinTable,则始终必须更新/保存/删除此侧,以安全地处理连接表中的条目(歌手和乐器之间的关系)。在这种情况下,您必须更新结束吉他课程的每个歌手。这不能正确反映关系的类型,并且可能会导致数据事务期间的性能问题和冲突。
不。双方都@ManyToMany
,但只有一方有@JoinTable
更多多对多信息在这里
要使@ManyToMany
处理现有模式(不是由 Hibernate 创建的),您必须在两个类上使用 @JoinTable 注释来指定表以及哪些列映射到相应类中的 Java 成员变量。我认为此示例可以帮助您确定应将哪些属性传递给注释:
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html