I've an entity Course
and an entity User
. There's a many-to-many relation ship between course and user, since a course can have many users and a user can be enrolled in many courses. In both entities I've put the @ManyToMany
annotation on the specific field, that is, in Course
I have:
@ManyToMany
private List<RegisteredUser> members;
and in User
I have:
@ManyToMany
private List<Course> coursesTaken;
Now, I know that this kind of many-to-many relationships are usually represented by a third table. I also know that there's the annotation @JoinTable
which allows us to do that. What I don't know is if I should add this annotation @JoinTable
over both fields in the two different entities or not. By the way, if I need to add to both, the names need to match right?
It's actually a good question, and it helps to understand the concept of an "owning" entity because neither side needs a @JoinTable
annotation. If you want to prevent both sides from having join tables
, a good idea, then you need to have a mappedBy=
element on one side. The @JoinTable
annotation is used to either specify the table name, or the columns that map the association.
First look at the Javadoc for @JoinTable:
Specifies the mapping of associations. It is applied to the owning side of an association.
Whether or not there is a join table
is controlled by the mappedBy="name"
element of the @ManyToMany
annotation. The Javadoc for mappedBy for the ManyToMany
annotation says:
The field that owns the relationship. Required unless the relationship is unidirectional.
For your (bidirectional) example in Hibernate (5.0.9.Final), if there were only two @ManyToMany
annotations and no mappedBy=
element, the default will have two Entity
tables and two 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))
While this is saying that each Entity "owns" its ManyToMany
relationship, the extra join table
is redundant in the typical use case. However, if I decide to have the Member
entity "own" the relationship, then I add the mappedBy=
element to the Course
entity to specify that it doesn't own the relationship:
@ManyToMany(mappedBy="courses")
Set<Member> members;
Adding @JoinTable(name="Member_Course")
to the Member
entity doesn't change anything: it's only naming the table the same as it would have been named anyway.
Since the Course
entity no longer owns its ManyToMany
relationship, the extra JoinTable
will not be created:
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))
This is important to the developer because he or she must understand that no relationship is persisted unless it's added to the owning entity, in this case the Member
entity. However, since this a bidirectional relationship, the developer should be adding both a Course
to Member.courses
and a Member
to Course.members
anyway.
So, if you have a bidirectional
ManyToMany
relationship, which means you have ManyToMany
on both entities involved, then you should add a mappedBy="name"
on one of them to avoid having a redundant join table
. Since it's bidirectional, I don't think it matters which side you make the owning
entity. As always, it's always a good idea to enable the sql logs and see what's going on in the database:
References:
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()?.