Понимание mappedBy аннотации в Hibernate
Я пытаюсь понять атрибут mappedBy
аннотации @OneToMany
в JPA. Я создал ниже пример, где у Клиента есть список Заказов:
@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;
}
Теперь, когда я использую Hibernate для генерации таблиц, я вижу, что Hibernate создал только 2 таблицы:
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
Также, если я попытаюсь сохранить клиента и некоторые Заказы, я вижу ниже инструкции DML, сгенерированные 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=?
Почему hibernate попытался вставить и обновить запись в TBL_ORDER вместо простого запроса на вставку?
Теперь, если я удалю атрибут mappedBy и попытаюсь сгенерировать таблицы, на этот раз я вижу 3 таблицы:
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
Почему hibernate создает дополнительную таблицу в этом случае? Как атрибут mappedBy контролирует это? Почему у нас есть дополнительное уникальное ограничение ключа для столбца orders_id в таблице Customer_TBL_ORDER?
Теперь, если я попытаюсь сохранить клиента и его заказы, я получу ниже операций DML:
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 (?, ?, ?)
Почему у нас нет дополнительного запроса на обновление в этом случае по сравнению с тем случаем, когда я объявил атрибут mappedBy?
Ответы
Ответ 1
Это нормальное.
С помощью mappedBy
вы напрямую указываете Hibernate/JPA, что одна таблица владеет этим отношением и поэтому хранится в виде столбца этой таблицы.
Без этого отношения внешние, а Hibernate/JPA необходимо создать другую таблицу для хранения отношения.
Пример:
- У stackoverflow
Question
есть несколько Answer
.
- An
Answer
принадлежит одному и только одному Question
.
В простом JDBC вы создадите две таблицы:
Questions(Question_ID, ...);
Answers(Answer_ID, Question_ID, ...);
Где Question_ID
- это внешний ключ, ссылающийся на Question.Question_ID
.
Как и в другом случае, у меня нет реального случая, поскольку почти каждый раз многие из них имеют уникальное ограничение (например: a Question
может иметь несколько Answer
, а Answer
может иметь физически имеют несколько Question
, но отображаются только один раз для любого Question
).
Ответ 2
A OneToMany
имеет два основных способа, которыми это может быть достигнуто в базе данных.
- с использованием таблицы отношений (M: M)
- с использованием внешнего ключа в целевой таблице. (1: М)
Если вы оставите отображение OneToMany
и зададите его по умолчанию, оно по умолчанию будет использовать таблицу отношений, которая, как вам кажется, вам нужна. Если вы не хотите использовать таблицу соединений, вы должны указать либо JoinColumn
, который устанавливает отношение внешнего ключа, как и OneToOne
, но в противоположном направлении - указывает провайдеру, какое поле в целевом таблица указывает на первичный ключ этого объекта. Но обычно целевой внешний ключ отображается в целевом объекте - он все равно ManyToOne
- и поэтому mappedby
используется для указания, что этот OneToMany
должен использовать отношение, уже определенное в целевом объекте.
В этом случае mappedBy=customer
говорит ему, чтобы посмотреть на отображение свойств customer
в объекте Order. Там он находит, что клиент имеет сопоставление ManyToMany
с по умолчанию joinColumn customer_id
, которое он также использует для OneToMany
. Поскольку он уже отображается в объекте Order, OneToMany
доступен только для чтения. Это гарантирует, что если два отношения не синхронизируются, JPA
имеет одну сторону, чтобы всегда доверять и использовать для установки поля базы данных.
Ответ 3
Нормально это поведение mappedBy
указывает только на то, что объект в этой стороне является обратным отношению, а владелец находится в "другой" сущности.
Я предлагаю вам ссылку