15

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?

Chaitanya
  • 15,403
  • 35
  • 96
  • 137

3 Answers3

23

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

NoDataFound
  • 11,381
  • 33
  • 59
13

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.

Kasun Siyambalapitiya
  • 3,956
  • 8
  • 38
  • 58
Chris
  • 20,138
  • 2
  • 29
  • 43
2

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

Xstian
  • 8,184
  • 10
  • 42
  • 72