Я просматриваю документацию Hibernate для двунаправленных отношений, в документе говорится, что:
Пример 7.21. Двунаправленная одна для многих со многими в одну сторону как владелец ассоциации
@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Отряд имеет двунаправленную связь со многими солдатами с солдатом. Вы не должны (не должны) определять какое-либо физическое сопоставление на стороне mappedBy.
Чтобы отобразить двунаправленную для многих, со стороны "один-ко-многим" в качестве стороны-владельца, вы должны удалить элемент mappedBy и установить много в один @JoinColumn как вставляемый и обновляемый на false. Это решение не оптимизировано и будет содержать дополнительные инструкции UPDATE.
Пример 7.22. Двунаправленная связь с одной до многих сторон как владелец
@Entity
public class Troop {
@OneToMany
@JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
Мне трудно понять это, поскольку я новичок в Hibernate.
1) Что это означает, когда документ говорит: You don't have to (must not) define any physical mapping in the mappedBy side.
2) @JoinColumn
в 7.22 имеет такое же значение (troop_fk) для атрибута name
. Можем ли мы указать разные значения? В чем преимущество и недостатки установки insertable=false, updatable=false
здесь?
Может кто-нибудь объяснить?
Это двунаправленная ассоциация. Итак, если солдат уходит в отряд, в отряд входит солдат. Это всего лишь два способа сказать одно и то же.
В Soldier вы сообщаете, как ассоциация представлена в базе данных: используя столбец объединения с именем troop_fk:
@JoinColumn(name="troop_fk")
public Troop getTroop() {
Таким образом, повторение этой же информации с другой стороны этой двунаправленной ассоциации является излишним. Вы не должны этого делать. Говоря
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
вы сообщаете Hibernate, что getSoldiers()
является обратной стороной двунаправленной связи и что способ сопоставления этой ассоциации можно найти в Soldier.troop
.
Что касается вашего второго вопроса. Еще раз, цель состоит в том, чтобы определить одну, но двунаправленную связь. Для сопоставления одной ассоциации вам не нужны два разных внешних ключа. Поэтому указание другого имени для столбца объединения не имеет смысла: оно создало бы другую, однонаправленную связь.
Этот способ - уродливый взлом, который AFAIK не поддерживается спецификацией JPA. Спецификация JPA предусматривает, что сторона владельца двунаправленной ассоциации OneToMany является большой стороной. Фактически он создает две однонаправленные ассоциации, сопоставленные одинаково, и сообщает Hibernate (используя insertable = false и updatable = false), чтобы игнорировать один из них при сохранении объекта. Он будет заполнять soldier.troop при чтении солдата из базы данных, но все, что вы введете в soldier.troop, будет игнорироваться при спасении солдата. Вы должны избегать этого способа, ИМХО.