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()?.
You actually CAN use @JoinTable on both sides and often it makes perfect sense! I am talking out of experience after I had been looking for this solution for weeks.
Even though all throughout the internet, blogs and articles tell a different story - and the Javadoc of JPA is easily misunderstood (or wrong) in this way. I tried it after seeing this uncommented example in a book for professionals - and it worked.
How to do it:
Singer-Instrument-Association: Singer side:
@ManyToMany
@JoinTable(name = "singer_instrument", joinColumns =
@JoinColumn(name = "SINGER_ID"), inverseJoinColumns = @JoinColumn(name = "INSTRUMENT_ID"))
public Set<Instrument> instruments;
And exactly the same on the other side! Instrument side:
@ManyToMany
@JoinTable(name = "singer_instrument",
joinColumns = @JoinColumn(name = "INSTRUMENT_ID"),
inverseJoinColumns = @JoinColumn(name = "SINGER_ID"))
public Set<Singer> singers;
So, if you address the same join table, "singer_instrument", with the same name, it work's. If you address one join table "singer_instrument" and one join table "instrument-singer" though, it will indead result in two different join tables in the database.
This makes a lot of sense, because a many-to-many relationship has no owning side - seen from the database perspective. Owning side means the side, that owns the foreign key of the relationship. But neither the table "singer" nor "instrument" have a foreign key referring to each other. The foreign keys are inside the neccessary join table between them.
The advantage of @JoinTable on both sides of the relation: Let's say, a singer starts to learn a new instrument: You can add the instrument to singer (and vise versa, as it is bidirectional) and update/merge the singer. The update will update only the singer and the join table. It won't touch the instrument-table.
Now the other case - a guitar-course has ended, so you want to remove the connection between the guitar and the former course-participants/singers: After removing the instrument "guitar" from the singers (and vise versa!), you update/merge the instrument. The update will update only the instrument and the join table. It won't touch the singer-table.
If you had @JoinTable only on one side, you would always have to update/save/delete this side to safely handle the entries in the join table (the relationships between singers and instruments). In this case, you would have to update each singer, who ended the guitar course. That is not reflecting the type of relationship properly and can cause performance issues and conflicts during data transaction.
Nope. Both sides get @ManyToMany
, but only one has the @JoinTable
More ManyToMany info here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With