Яка різниця між JOIN та JOIN FETCH при використанні JPA та Hibernate


183

Будь ласка, допоможіть мені зрозуміти, де використовувати звичайний ПРИЄДНАЙТЕСЬ та де ПРИЄДНАЙТЕ ПОДОБ.

Наприклад, якщо у нас є ці два запити

FROM Employee emp
JOIN emp.department dep

і

FROM Employee emp
JOIN FETCH emp.department dep

Чи є різниця між ними? Якщо так, то який саме використовувати коли?


2
ви можете знайти тут посилання, прочитане 14.3. Асоціації та приєднання
Angga

4
Я пройшов цю документаційну документацію, але досі не знаю, де я повинен використовувати СПОЛУЧЕННЯ та де ПРИЄДНАЙТЕ ПІДХОД.
абас

2
Якщо для відображення @oneToOne встановлено FetchType.LAZY, і ви використовуєте другий запит (тому що вам потрібно завантажувати об’єкти відділу як частину об’єктів Employee), що робитиме Hibernate, він видаватиме запити для отримання об’єктів відділу для кожного окремого об'єкта працівника він отримує з БД. Пізніше в коді ви можете отримати доступ до об'єктів Департаменту через однозначну асоціацію Відділення до відділу, і Hibernate не видасть жодного запиту для отримання об’єкта Департаменту для даного Співробітника. Пам'ятайте, що сплячий режим все ще видає запити, що дорівнює кількості працівників, яких він отримав.
Bunti

На допомогу в охоті на doc ~ Вибір стратегій
Едді Б

1
@ShameeraAnuranga Я думаю, що в цьому випадку вам знадобиться ЛІВНІЙ ЗОВНІШНІЙ ПРИЄДНАЙТЕСЬ.
Аббас

Відповіді:


181

У цих двох запитах ви використовуєте JOIN для запиту всіх співробітників, які мають принаймні один відділ.

Але різниця полягає в тому, що в першому запиті ви повертаєте лише службовців для сплячого. У другому запиті ви повертаєте службовців та всі пов'язані з ними відділи.

Отже, якщо ви використовуєте другий запит, вам не потрібно буде робити новий запит, щоб знову потрапити в базу даних, щоб побачити підрозділи кожного співробітника.

Ви можете використовувати другий запит, коли впевнені, що вам знадобиться відділ кожного працівника. Якщо вам не потрібен відділ, використовуйте перший запит.

Я рекомендую прочитати це посилання, якщо вам потрібно застосувати якусь умову WHERE (що вам, мабуть, знадобиться): Як правильно виразити JPQL "приєднатись до отримання" з "де" пунктом як JPA 2 CriteriaQuery?

Оновлення

Якщо ви не користуєтесь, fetchа Департаменти продовжують повертатись, це тому, що ваше відображення між Співробітником та Департаментом (а @OneToMany) встановлено FetchType.EAGER. У цьому випадку будь-який fetchзапит HQL (із запитом чи ні) FROM Employeeпринесе всі відділи. Пам’ятайте, що всі відображення * ToOne ( @ManyToOneі @OneToOne) за замовчуванням є EAGER.


1
Яка поведінка буде, якщо ми виконаємо заяву без отримання та отримаємо результат. Тоді в межах сесії ми будемо лікуватися у відділенні?
gstackoverflow

1
@gstackoverflow, так
Dherik

Я використовую рідний запит із Lazy fetch в обох сторонах стосунків, але все ще завантажує ієрархію стосунків дітей.
Бадамчі

Варто згадати, що fetchпотрібно використовувати, якщо (використовуючи наш приклад) ви хочете замовити якийсь атрибут Відділу. В іншому випадку (дійсно принаймні для PG) ви можете отриматиERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
довгий

60

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

Об’єднання "отримання" дозволяє асоціації або колекції значень ініціалізуватися разом із їхніми батьківськими об'єктами за допомогою одного вибору. Це особливо корисно у випадку з колекцією. Це ефективно перекриває зовнішнє об'єднання та ліниві декларації картографічного файлу для асоціацій та колекцій.

це "ПРИЄДНУЙТЕ ФЕТЧ" матиме ефект, якщо ви маєте (fetch = FetchType.LAZY) властивість для колекції всередині сутності (наприклад, нижче).

І це лише ефект методу "коли запит має відбутися". І ви також повинні знати це :

сплячий має два ортогональних поняття: коли відбувається асоціація і як вона виймається. Важливо, щоб ви не переплутали їх. Ми використовуємо функцію для налаштування продуктивності. Ми можемо використовувати ледачий, щоб визначити договір, які дані завжди доступні в будь-якому окремому екземплярі певного класу.

коли вибирається асоціація -> ваш тип "ВИКОНАННЯ"

як це витягнуто -> Приєднатися / вибрати / Підібрати / Пакет

У вашому випадку FETCH матиме ефект лише в тому випадку, якщо у вас є відділ як набір у співробітнику, щось подібне в організації:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

коли ви використовуєте

FROM Employee emp
JOIN FETCH emp.department dep

ви отримаєте empі emp.dep. коли ви не користуєтеся добутком, ви все одно можете отримати, emp.depале сплячий режим обробить інший вибір до бази даних, щоб отримати цей набір відділу.

тож це лише питання налаштування продуктивності, про те, що ви хочете отримати весь результат (вам це потрібно чи ні) в одному запиті (нетерплячий видобуток), або ви хочете запитувати його останнім, коли вам це потрібно (лінивий вибір).

Використовуйте нетерплячий пошук, коли вам потрібно отримати невеликі дані за допомогою одного вибору (один великий запит). Або скористайтеся ледачим пошуком, щоб запитати, що вам потрібно останнім (багато менших запитів).

використовувати fetch, коли:

  • немає великої непотрібної колекції / набору всередині тієї організації, яку ви збираєтеся отримати

  • спілкування від сервера додатків до сервера баз даних занадто далеко і потребує тривалого часу

  • вам може знадобитися , що збір останнього , коли у вас немає доступу до нього ( за межі від транзакційного методу / класу)


Чи можете ви пояснити це для запитів, які я щойно написав у оновленому запитанні.
абас

корисний розгляд: "немає великої непотрібної колекції / набору в тій особі, яку ви збираєтеся отримати"
Divs

Чи все-таки департаменти будуть нетерпляче зібрані, якби підрозділи всередині працівника були Listзамість Set?
Стефан

Чи означає використання FETCHключового слова в операторі JPQL, яке охоче отримує властивість?
Стефан

15

ПРИЄДНАЙТЕСЬ

При використанні JOINпроти асоціацій суб'єктів, JPA генерує JOIN між батьківською сутністю та таблицями дочірньої сутності у створеному операторі SQL.

Отже, беручи свій приклад, виконуючи цей запит JPQL:

FROM Employee emp
JOIN emp.department dep

Hibernate буде генерувати такий оператор SQL:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Зауважте, що SELECTпункт SQL містить лише employeeстовпці таблиці, а не departmentті. Для отримання departmentстовпців таблиці нам потрібно використовувати JOIN FETCHзамість JOIN.

ПРИЄДНАЙТЕСЬ ДО ПОДІЙ

Отже, порівняно з JOIN, це JOIN FETCHдозволяє проектувати стовпці таблиці приєднання в SELECTпункті згенерованого оператора SQL.

Отже, у вашому прикладі при виконанні цього запиту JPQL:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate буде генерувати такий оператор SQL:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Зауважте, що цього разу departmentвибираються також стовпці таблиці, а не лише ті, що асоціюються з сутністю, переліченою у FROMпункті JPQL.

Крім того, JOIN FETCHце чудовий спосіб вирішити питання LazyInitializationExceptionщодо використання в режимі Hibernate, оскільки ви можете ініціалізувати асоціації об'єктів, використовуючи FetchType.LAZYстратегію отримання, а також основну сутність, яку ви отримуєте.


Чи можливо використовувати декілька JOIN FETCH в одному запиті?
А.Онур Оскан

2
Ви можете приєднатися до отримання декількох асоціацій «багато в одному» та «один до одного» та щонайбільше до однієї колекції. Отримання декількох колекцій, наприклад асоціацій один-багато-багато-багато-багато, потрапляє в декартовий продукт . Однак якщо ви хочете отримати кілька колекцій, ви можете використовувати вторинні запити для другої, третьої, ..., n-ї колекцій. Перегляньте цю статтю для отримання більш детальної інформації.
Влад Михальча

5

Якщо у вас встановлено @oneToOneвідображення FetchType.LAZYі ви використовуєте другий запит (оскільки вам потрібно завантажувати об’єкти відділу як частину об'єктів Employee), що робитиме Hibernate, це видасть запити для отримання об’єктів відділу для кожного окремого об'єкта Employee, який він отримує з БД.

Пізніше в коді ви можете отримати доступ до об'єктів Департаменту через службовця відділу до однозначного об'єднання, і Hibernate не видасть жодного запиту для отримання об’єкта Департаменту для даного Співробітника.

Пам'ятайте, Hibernate все ще видає запити, що дорівнює кількості працівників, яких вона отримала. Hibernate видасть однакову кількість запитів в обох вищезазначених запитах, якщо ви хочете отримати доступ до об'єктів відділу всіх об'єктів працівника


2

Дерік: Я не впевнений у тому, що ви говорите, коли ви не будете використовувати результат, результат буде мати тип: List<Object[ ]>що означає перелік таблиць об'єктів, а не список співробітників.

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

Якщо ви користуєтеся програмою "Вибір", вибирається лише один вибір, а результат - список "Співробітник" List<Employee> що містить перелік відділень. Це переосмислює ледачу заяву сутності.


Я не знаю, чи я розумію вашу стурбованість. Якщо ви не використовуєте fetch, ваш запит повертають лише працівники. Якщо Департаменти, навіть у цьому випадку, продовжують повертатися, це тому, що ваше зіставлення між співробітником і відділом (@OneToMany) встановлено FetchType.EAGER. У цьому випадку будь-який fetchзапит HQL (із запитом чи ні) FROM Employeeпринесе всі відділи.
Дерік

Без використання вибору (лише термін приєднання) результатом буде масив колекцій, два ряди, перший - колекція співробітників, другий - колекція відділів. Використовуючи нетерплячий вибор чи ледачий збір, вийдуть відділи.
Білал БББ

Без отримання HQL це станеться лише в тому випадку, якщо ваше зіставлення між співробітником і відділом буде EAGER ( @OneToMany(fetch = FetchType.EAGER). Якщо це не так, Департаменти не повертаються.
Дерік

@Дерик спробуй сам, ти отримаєш ClassCastException.
Білал БББ

Я з'ясовував проблему. Це не проблема вибору, а те, як selectбуло зроблено в HQL. Спробуйте SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate мають таку поведінку повертають Listз Object[]коли ви ommit на SELECTчастини.
Дерік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.