I am trying to understand the mappedBy
attribute of @OneToMany
annotation in JPA. I created below example where a Customer has a list of Orders:
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@OrderColumn(name="orders_index")
public List<Order> getOrders() { return orders; }
public void setOrders(List<Order> orders) { this.orders = orders; }
private List<Order> orders;
}
@Entity
@Table(name="TBL_ORDER")
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public int getOrderNumber() { return orderNumber; }
public void setOrderNumber(int orderNumber) { this.orderNumber = orderNumber; }
private int orderNumber;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer customer;
}
Now when I use Hibernate to generate the tables then I see that Hibernate created only 2 tables:
Hibernate: create table Customer (id number(10,0) not null, primary key (id))
Hibernate: create table TBL_ORDER (id number(10,0) not null, orderNumber number(10,0) not null, customer_id number(10,0), orders_index number(10,0), primary key (id))
Hibernate: alter table TBL_ORDER add constraint FK_nt24krtgqwcsynosqgk4jkvfv foreign key (customer_id) references Customer
Also if I try to save a customer and some Orders I see below DML statements generated by Hibernate:
Hibernate: insert into Customer (id) values (?)
Hibernate: insert into TBL_ORDER (customer_id, orderNumber, id) values (?, ?, ?)
Hibernate: update TBL_ORDER set orders_index=? where id=?
Why hibernate tried to insert and updates the record in TBL_ORDER instead of just running a single insert query?
Now if I remove the mappedBy attribute and try to generate the tables, I see 3 tables this time:
Hibernate: create table Customer (id number(10,0) not null, primary key (id))
Hibernate: create table Customer_TBL_ORDER (Customer_id number(10,0) not null, orders_id number(10,0) not null, orders_index number(10,0) not null, primary key (Customer_id, orders_index))
Hibernate: create table TBL_ORDER (id number(10,0) not null, orderNumber number(10,0) not null, customer_id number(10,0), primary key (id))
Hibernate: alter table Customer_TBL_ORDER add constraint UK_sw94jktvh72tripj876s31052 unique (orders_id)
Hibernate: alter table Customer_TBL_ORDER add constraint FK_sw94jktvh72tripj876s31052 foreign key (orders_id) references TBL_ORDER
Hibernate: alter table Customer_TBL_ORDER add constraint FK_f03up2h945cg0dcbo2pdb1d3c foreign key (Customer_id) references Customer
Hibernate: alter table TBL_ORDER add constraint FK_nt24krtgqwcsynosqgk4jkvfv foreign key (customer_id) references Customer
Why hibernate creates an additional table in this case? How the mappedBy attribute is controlling this? Why we have an additional unique key constraint on orders_id column in Customer_TBL_ORDER table?
Now if I try to save a customer and its orders I get below DML operations:
Hibernate: insert into Customer (id) values (?)
Hibernate: insert into TBL_ORDER (customer_id, orderNumber, id) values (?, ?, ?)
Hibernate: insert into Customer_TBL_ORDER (Customer_id, orders_index, orders_id) values (?, ?, ?)
Why we don't have an additional update query in this case compared to the case where I have declared the mappedBy attribute?
mappedBy tells Hibernate how to create instances of your entities and load the data into them. It should refer to the field name in the class that you are annotating, PersonDetail in this instance, where the relationship is defined.
The purpose of the MappedBy parameter is to instruct JPA: Do NOT create another join table as the relationship is already being mapped by the opposite entity of this relationship.
The @JoinColumn annotation helps us specify the column we'll use for joining an entity association or element collection. On the other hand, the mappedBy attribute is used to define the referencing side (non-owning side) of the relationship.
If the relationship is bidirectional, the non-owning side must use the mappedBy element of the OneToOne annotation to specify the relationship field or property of the owning side. The OneToOne annotation may be used within an embeddable class to specify a relationship from the embeddable class to an entity class.
That's normal.
With the mappedBy
, you directly tell Hibernate/JPA that one table owns the relationship, and therefore it is stored as a column of that table.
Without, the relationship is external and Hibernate/JPA need to create another table to store the relationship.
Example:
Question
have several Answer
.Answer
is owned by one and only one Question
.In plain JDBC, you would create two table:
Questions(Question_ID, ...);
Answers(Answer_ID, Question_ID, ...);
Where Question_ID
is foreign key referencing Question.Question_ID
.
As for the other case, I don't have a real case since there are almost every time many to many with a unique constraint (eg: a Question
may have several Answer
, and Answer
may have physically have several Question
, but appears only once for any Question
).
A OneToMany
has two main ways it can be accomplished in the database.
If you leave a OneToMany
mapping and let it default, it will by default use a relation table, which is what you seem to want. If you don't want a join table to be used, you would either specify a JoinColumn
which sets up a foreign key relationship much like a OneToOne
, but in the opposite direction - it tells the provider what field in the target table points to this entity's primary key. But commonly the target foreign key is mapped in the target entity - it is a ManyToOne
after all - and so the mappedby
is used to specify that this OneToMany
should use the relationship already defined in the target entity.
In this case, mappedBy=customer
tells it to look at the customer
property mapping within the Order entity. There, it finds customer has a ManyToMany
mapping with a default joinColumn customer_id
that it also uses for the OneToMany
. Because it is already mapped within the Order entity, the OneToMany
is read-only. This ensures that should the two relationships be out of sync, JPA
has one side to always trust and use to set the database field.
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