diff --git a/dev/java/hibernate/Дублирование значений при использовании @ElementCollection и @OneToMany.md b/dev/java/hibernate/Дублирование значений при использовании @ElementCollection и @OneToMany.md new file mode 100644 index 00000000..7eb6924c --- /dev/null +++ b/dev/java/hibernate/Дублирование значений при использовании @ElementCollection и @OneToMany.md @@ -0,0 +1,48 @@ +--- +aliases: +tags: + - maturity/🌱 +date: + - - 2024-09-06 +zero-link: + - "[[../../../meta/zero/00 Hibernate|00 Hibernate]]" +parents: +linked: +--- +Столкнулся с неочевидным поведением `@ElementCollection` в связке с `@OneToMany`. Может случиться так, что в `@OneToMany` будет дублирование значений из-за `@ElementCollection`. Проще объяснить на примере. + +Допустим у нас есть три таблицы: `user`, `user_nickname`, `address`. Есть сущность `User`: + +```java +@Entity +public class User { + + // ... ... ... ... ... + + @OneToMany(mappedBy = "user", fetch = FetchType.EAGER) + private List
stages = new ArrayList<>(); + + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name = "user_nickname", + joinColumns = @JoinColumn(name = "user_id") + ) + @Column(name = "nickname") + private Set nicknames = new HashSet<>(); + + // ... ... ... ... ... + +} +``` + +Допустим у нас одна запись про пользователя, у которого есть три адреса и один никнейм. В этом случае все хорошо, все придет корректно. + +==Добавим этому пользователю еще один никнейм. Теперь мы получаем шесть адресов и два никнейма. Откуда взялись еще три лишних адреса, в базе данных их все еще три.== Это дубликаты. При этом, если вы воспользуетесь пагинацией JPA, то дублей уже не будет. It is magic 💫 + +Магии в программировании, как вы понимаете, нет. Они создаются при использовании `FetchType.EAGER` у `@OneToMany` в совокупности с `@ElementCollection(fetch = FetchType.EAGER)`. Hibernate генерирует запрос с двумя-тремя полными соединениями, отсюда и берутся дубли. При этом в пагинации Hibernate не генерирует джойны, а использует кучу селектов, отсюда и отсутствие дублей при пагинации. + +Эта проблема решается несколькими способами: + +- Переделайте `List` в `Set` у `@OneToMany`. +- Уберите `FetchType.EAGER` у `@OneToMany`. +- Добавьте `@Fetch(FetchMode.SUBSELECT)` у `@OneToMany`. это аннотация Hibernate, которая вместо JOIN использует подзапрос. О [подзапросах я писал в отдельной статье](https://struchkov.dev/blog/ru/select-subquery). \ No newline at end of file diff --git a/meta/zero/00 Hibernate.md b/meta/zero/00 Hibernate.md index 2c0145ed..689f67d0 100644 --- a/meta/zero/00 Hibernate.md +++ b/meta/zero/00 Hibernate.md @@ -5,5 +5,5 @@ tags: - type/zero-link title: Hibernate --- -- [Дублирование значений при использовании @ElementCollection и @OneToMany](../../../../_inbox/Дублирование%20значений%20при%20использовании%20@ElementCollection%20и%20@OneToMany.md) +- [Дублирование значений при использовании @ElementCollection и @OneToMany](../../dev/java/hibernate/Дублирование%20значений%20при%20использовании%20@ElementCollection%20и%20@OneToMany.md) - [Логирование SQL в Hibernate](../../dev/java/hibernate/Логирование%20SQL%20в%20Hibernate.md) \ No newline at end of file