Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding mappedBy annotation in Hibernate

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?

like image 671
Chaitanya Avatar asked Aug 18 '14 20:08

Chaitanya


People also ask

What does mappedBy mean in Hibernate?

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.

What is the use of mappedBy in JPA?

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.

What is the difference between @JoinColumn and mappedBy?

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.

What is mappedBy in OneToOne?

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.


2 Answers

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.

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

like image 112
NoDataFound Avatar answered Sep 16 '22 15:09

NoDataFound


A OneToMany has two main ways it can be accomplished in the database.

  1. using a relation table (M:M)
  2. using a foreign key in the target table. (1:M)

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 OneToManyshould 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.

like image 32
Chris Avatar answered Sep 17 '22 15:09

Chris