I am trying to understand the mappedBy
attribute of @OneToMany
annotation in JPA. I created below example where a Customer
has a list of Order
s:
@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?
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:
A stackoverflow Question
have several Answer
.
An 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
.
A OneToMany has two main ways it can be accomplished in the database.
- using a relation table (M:M)
- using a foreign key in the target table. (1:M)
If you leave a OneToMany mapping and let it default, it will default to using 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 oposite 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.
Is normal this behavior mappedBy indicates only that the entity in this side is the inverse of the relationship, and the owner resides in the “other” entity.
I suggest you this link