MappedBy in bi-directional @ManyToMany : what is the reason
Asked Answered
V

2

13
  1. What is the reason for setting MappedBy in bidirectional many-to-many relationships?
  2. When one table has significant amount of records, while other has a few, which side is better to put mappedBy?
Villasenor answered 15/5, 2016 at 20:10 Comment(0)
B
30

It's actually a good question, and it helps to understand the concept of an "owning" entity. If you want to prevent both sides (in a bidirectional relationship) from having join tables, a good idea, then you need to have a mappedBy= element on one side.

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, 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 SideA (id bigint not null, primary key (id))
Hibernate: create table SideA_SideB (sidea_id bigint not null, sidebs_id bigint not null, primary key (sidea_id, sidebs_id))
Hibernate: create table SideB (id bigint not null, primary key (id))
Hibernate: create table SideB_SideA (sideb_id bigint not null, sideas_id bigint not null, primary key (sideb_id, sideas_id))

While this is saying that each Entity "owns" its ManyToMany relationship, the extra join table is redundant in the typical use case, and the Javadoc says you need a mappedBy annotation. If I decide to have SideA "own" the relationship, then I add the mappedBy= element to the SideB entity to specify that it doesn't own the relationship:

@Entity
public class SideA {
    @ManyToMany
    Set<SideB> sidebs;
}
@Entity
public class SideB {
    @ManyToMany(mappedBy="sidebs")
    Set<SideA> sideas;
}

Since the SideB entity no longer owns its ManyToMany relationship, the extra JoinTable will not be created:

Hibernate: create table SideA (id bigint not null, primary key (id))
Hibernate: create table SideB (id bigint not null, primary key (id))
Hibernate: create table SideA_SideB (sideas_id bigint not null, sidebs_id bigint not null, primary key (sideas_id, sidebs_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 SideA entity.

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 as per the Javadoc and to avoid having a redundant join table.

As to which side to make the owning entity, there is no correct answer, it depends on what your system thinks is best. The relationship will only be persisted when entries are put in the owning side so you have to ask yourself whether you more commonly change a SideA's list or SideB's list. If SideA owns the relationship then you update the relationship by adding or removing SideB instances from a SideA instance but if you had a list of SideA instances for a SideB that you wanted to persist you would need to iterate through the list and alter each instance of SideA in the list.

As always, it's always a good idea to enable the sql logs and see what's going on in the database:

EDIT: If you have a persistence provider that only creates a single join table with no mappedBy setting then you have to check with the docs to see which side "owns" the relationship. Could be that neither or both sides own it and that updating neither or either side will persist the entity.

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

Barragan answered 19/5, 2016 at 0:46 Comment(5)
Interestingly enough, ebean 4.0.2 on Play! 2.6 creates only one table even without mappedBy on any sideAbstriction
Still confused because it is creating only one join-table without mappedByContinue
The use of @JoinTable annotations will affect the creating of join tables and this answer does not address that.Barragan
"As always, it's always a good idea to enable the sql logs and see what's going on in the database:" Great advice. I still don't understand why does the non-owning side have to be aware of the name of the field in the owning side, especially when changes to the non owning side are not reflected to the database.Deoxygenate
If there is no mappedBy attribute the relation is assumed to be the owner. Adding mappedBy tells a JPA not to invoke any persistance functions when the property changes. ManyToOne does not have a mappedBy attribute because it is always the owner of the relation because that is where the FK is. OneToMany needs it otherwise without it persistence functions will be called anytime the relation (the Set) is changed which would be poor SQL performance and probably other problems. Not only enable the logs but also think in terms of JDBC/SQL because that is ultimately what happens.Barragan
M
1

mappedBy links both sides of a BIDIRECTIONAL relation. You put mappedBy on the OWNER of the relation, not based on how many records something has (aka object oriented design). You will find this information in any JPA tutorial and documentation.

Machinery answered 16/5, 2016 at 5:38 Comment(3)
Documentation says there is no difference where to put mappedBy, i.e. I can choose any side as owner. I have not clear understanding why there is a need for mappedBy in many-to-many. It is clear for many-to-one/one-to-many. I am suspect affect on productivity, and it is the reason of the 2nd part of my question.Villasenor
There is a need in a BIDIRECTIONAL relation, as I said. The 1-N, 1-1, M-N is irrevelant to that question. Which side defines the OWNER. See the JPA specMachinery
Actually you put mappedBy on the REVERSE side of the relationPashalik

© 2022 - 2024 — McMap. All rights reserved.