Should @JoinTable be specified in both sides of a @ManyToMany relationship?

nbro picture nbro · Apr 22, 2016 · Viewed 8.6k times · Source

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?

Answer

K.Nicholas picture K.Nicholas · Apr 25, 2016

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()?.