Вопрос-ответ

JPA JoinColumn vs mappedBy

JPA JoinColumn vs mappedBy

В чем разница между:

@Entity
public class Company {

@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
@JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
private List<Branch> branches;
...
}

и

@Entity
public class Company {

@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "companyIdRef")

private List<Branch> branches;
...
}
Переведено автоматически
Ответ 1

Аннотация @JoinColumn указывает, что эта сущность является владельцем связи (то есть: соответствующая таблица имеет столбец с внешним ключом к таблице, на которую ссылается ссылка), тогда как атрибут mappedBy указывает, что сущность на этой стороне является обратной связи, а владелец находится в "другой" сущности. Это также означает, что вы можете получить доступ к другой таблице из класса, который вы аннотировали с помощью "mappedBy" (полностью двунаправленная связь).

В частности, для кода в вопросе правильные аннотации будут выглядеть следующим образом:

@Entity
public class Company {
@OneToMany(mappedBy = "company",
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)

private List<Branch> branches;
}

@Entity
public class Branch {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "companyId")
private Company company;
}
Ответ 2

@JoinColumn может использоваться с обеих сторон связи. Вопрос был об использовании @JoinColumn на @OneToMany стороне (редкий случай). И дело здесь в физическом дублировании информации наряду с неоптимизированным SQL-запросом, который выдаст некоторые дополнительные UPDATE инструкции.

Согласно документации:

Поскольку в спецификации JPA двунаправленные отношения многие к одному (почти) всегда находятся на стороне владельца, ассоциация "один ко многим" аннотируется @OneToMany(mappedBy=...)

@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}

@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}

Troop имеет двунаправленную связь "один ко многим" с Soldier через свойство troop . Вам не нужно (не должно) определять какое-либо физическое отображение на стороне mappedBy.

Чтобы сопоставить двунаправленный параметр "один ко многим" со стороной "один ко многим" в качестве стороны-владельца, вы должны удалить mappedBy элемент и установить для параметра "много" значение "один" @JoinColumn как insertable и updatable значение false. Это решение не оптимизировано и приведет к появлению некоторых дополнительных UPDATE инструкций.

@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() {
...
}
Ответ 3

Однонаправленная ассоциация "один ко многим"

Если вы используете @OneToMany аннотацию с @JoinColumn, то у вас будет однонаправленная связь, подобная связи между родительским Post объектом и дочерним PostComment объектом на следующей диаграмме:

Однонаправленная ассоциация "один ко многим"

При использовании однонаправленной ассоциации "один ко многим" сопоставляет ассоциацию только родительская сторона.

В этом примере только Post объект будет определять @OneToMany ассоциацию с дочерним PostComment объектом:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();

Двунаправленная ассоциация "один ко многим"

Если вы используете @OneToMany с mappedBy набором атрибутов, у вас получается двунаправленная связь. В нашем случае как у Post объекта есть коллекция PostComment дочерних объектов, так и у дочернего PostComment объекта есть обратная ссылка на родительский Post объект, как показано на следующей диаграмме:

Двунаправленная ассоциация "один ко многим"

В PostComment сущности свойство post entity отображается следующим образом:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

Причина, по которой мы явно устанавливаем для fetch атрибута значение FetchType.LAZY, заключается в том, что по умолчанию все @ManyToOne и @OneToOne ассоциации извлекаются быстро, что может вызвать N + 1 проблем с запросами.


В Post сущности comments ассоциация отображается следующим образом:

@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)

private List<PostComment> comments = new ArrayList<>();

mappedBy Атрибут @OneToMany аннотации ссылается на post свойство дочерней PostComment сущности, и, таким образом, Hibernate знает, что двунаправленная связь контролируется @ManyToOne стороной, которая отвечает за управление значением столбца внешнего ключа, на котором основана эта табличная связь.

Для двунаправленной ассоциации вам также необходимо иметь два служебных метода, таких как addChild и removeChild:

public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}

public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}

Эти два метода гарантируют, что обе стороны двунаправленной ассоциации синхронизированы. Без синхронизации обоих концов Hibernate не гарантирует, что изменения состояния ассоциации будут распространяться на базу данных.

Какой из них выбрать?

Однонаправленная @OneToMany ассоциация работает не очень хорошо, поэтому вам следует избегать ее.

Вам лучше использовать двунаправленный @OneToMany который более эффективен.

Ответ 4

Я не согласен с принятым здесь ответом Оскара Лопеса. Этот ответ неточен!

Это НЕ @JoinColumn который указывает, что эта сущность является владельцем отношения. Вместо этого это @ManyToOne аннотация, которая делает это (в его примере).

Аннотации отношений, такие как @ManyToOne, @OneToMany и @ManyToMany, указывают JPA / Hibernate создать сопоставление. По умолчанию это делается с помощью отдельной таблицы соединений.



@JoinColumn


Цель @JoinColumn - создать объединенный столбец, если он еще не существует. Если это так, то эту аннотацию можно использовать для именования объединяемого столбца.




mappedBy


Цель MappedBy параметра - указать JPA: НЕ создавать другую таблицу объединения, поскольку связь уже сопоставляется противоположной сущности этой связи.





Помните: MappedBy это свойство аннотаций отношений, целью которого является создание механизма для связи двух сущностей, что по умолчанию они делают путем создания таблицы соединений. MappedBy останавливает этот процесс в одном направлении.

Объект, не использующий MappedBy, считается владельцем отношения, потому что механика сопоставления определяется внутри его класса посредством использования одной из трех аннотаций сопоставления для поля внешнего ключа. Это не только определяет характер сопоставления, но и инструктирует к созданию таблицы соединений. Кроме того, также существует возможность подавления таблицы объединения путем применения аннотации @JoinColumn к внешнему ключу, которая вместо этого сохраняет его внутри таблицы объекта-владельца.

Итак, вкратце: @JoinColumn либо создает новый объединяемый столбец, либо переименовывает существующий; в то время как MappedBy параметр работает совместно с аннотациями отношений другого (дочернего) класса, чтобы создать сопоставление либо через таблицу объединения, либо путем создания столбца внешнего ключа в связанной таблице объекта-владельца.

Чтобы проиллюстрировать, как MapppedBy работает, рассмотрим приведенный ниже код. Если бы MappedBy параметр нужно было удалить, то Hibernate фактически создал бы ДВЕ таблицы соединений! Почему? Поскольку в отношениях "многие ко многим" существует симметрия, а у Hibernate нет обоснования для выбора одного направления вместо другого.

Поэтому мы используем MappedBy, чтобы сообщить Hibernate, что мы выбрали другую сущность, чтобы диктовать отображение отношений между двумя сущностями.

@Entity
public class Driver {
@ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}

@Entity
public class Cars {
@ManyToMany
private List<Drivers> drivers;
}

Добавление @JoinColumn(name = "DriverID") в класс owner (см. Ниже) предотвратит создание таблицы соединений и вместо этого создаст столбец внешнего ключа DriverID в таблице Cars для построения сопоставления:

@Entity
public class Driver {
@ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}

@Entity
public class Cars {
@ManyToMany
@JoinColumn(name = "driverID")
private List<Drivers> drivers;
}
java hibernate jpa