В чем разница между JOIN и JOIN FETCH при использовании JPA и Hibernate

183

Пожалуйста, помогите мне понять, где использовать обычный JOIN, а где JOIN FETCH.

Например, если у нас есть эти два запроса

FROM Employee emp
JOIN emp.department dep

и

FROM Employee emp
JOIN FETCH emp.department dep

Есть ли разница между ними? Если да, какой использовать когда?

аббас
источник
2
Вы можете найти его здесь ссылку чтения 14.3. Ассоциации и объединения
Angga
4
Я прошел эту документацию, но до сих пор не знаю, где я должен использовать JOIN и где JOIN FETCH.
Аббас
2
Если для отображения @oneToOne установлено значение FetchType.LAZY и вы используете второй запрос (потому что вам нужно загружать объекты Department как часть объектов Employee), то, что будет делать Hibernate, будет выдавать запросы на выборку объектов Department для каждого отдельного объекта Employee. он извлекается из БД. Позже в коде вы можете получить доступ к объектам Department через однозначную связь Employee с Department, и Hibernate не будет выдавать никаких запросов для получения объекта Department для данного Employee. Помните, что Hibernate по-прежнему выдает запросы, равные количеству выбранных сотрудников.
Бунти
Чтобы помочь в охоте на док ~ Fetching Strategies
Eddie B
1
@ ShameeraAnuranga Я думаю, что в этом случае вам понадобится LEFT OUTER JOIN.
аббас

Ответы:

181

В этих двух запросах вы используете JOIN для запроса всех сотрудников, у которых есть хотя бы один отдел.

Но разница в следующем: в первом запросе вы возвращаете только сотрудников для Hibernate. Во втором запросе вы возвращаете сотрудников и все связанные отделы.

Таким образом, если вы используете второй запрос, вам не нужно будет делать новый запрос, чтобы снова попасть в базу данных, чтобы увидеть отделы каждого сотрудника.

Вы можете использовать второй запрос, когда вы уверены, что вам потребуется отдел каждого сотрудника. Если вам не нужен отдел, используйте первый запрос.

Я рекомендую прочитать эту ссылку, если вам нужно применить некоторое условие WHERE (что вам, вероятно, понадобится): как правильно выразить JPQL "join fetch" с предложением "where" как JPA 2 CriteriaQuery?

Обновить

Если вы не используете fetchи Департаменты продолжают возвращаться, это потому, что для вашего отображения между Сотрудником и Отделом (а @OneToMany) установлено значение FetchType.EAGER. В этом случае любой HQL- fetchзапрос (с или без) FROM Employeeпринесет все отделы. Помните, что все сопоставления * ToOne ( @ManyToOneи @OneToOne) по умолчанию являются EAGER.

Dherik
источник
1
Какое поведение будет, если мы выполним оператор без выборки и получим результат. Тогда в рамках сессии будем относиться к отделу?
gstackoverflow
1
@gstackoverflow, да
Дерик
Я использую нативный запрос с Lazy fetch для обеих сторон отношений, но все еще загружает иерархию дочерних отношений.
DBV
Стоит отметить, что fetchэто нужно использовать, если (используя наш пример) вы хотите упорядочить по некоторому атрибуту Department. В противном случае (действителен по крайней мере для PG) вы можете получитьERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
длинный
60

в этой ссылке, которую я упоминал ранее в комментарии, прочитайте эту часть:

Соединение «извлечение» позволяет инициализировать ассоциации или коллекции значений вместе с их родительскими объектами, используя один выбор. Это особенно полезно в случае коллекции. Он эффективно переопределяет внешние объединения и ленивые объявления файла сопоставления для ассоциаций и коллекций.

этот «JOIN FETCH» будет иметь эффект, если у вас есть свойство (fetch = FetchType.LAZY) для коллекции внутри сущности (пример ниже).

И это только эффект метода «когда должен произойти запрос». И вы должны также знать это :

Спящий режим имеет два ортогональных понятия: когда выбирается ассоциация и как она получается. Важно, чтобы вы их не путали. Мы используем fetch для настройки производительности. Мы можем использовать ленивый, чтобы определить контракт, для которого данные всегда доступны в любом отдельном экземпляре определенного класса.

когда выбирается ассоциация -> ваш тип "FETCH"

как это получается -> Присоединиться / выбрать / выбрать / пакет

В вашем случае, FETCH будет иметь эффект, только если у вас есть отдел как набор внутри Employee, что-то вроде этого в сущности:

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

когда вы используете

FROM Employee emp
JOIN FETCH emp.department dep

вы получите empи emp.dep. когда вы не использовали fetch, вы все равно можете получить, emp.depно hibernate обработает другой выбор в базе данных, чтобы получить этот набор отделов.

так что это просто вопрос настройки производительности, о том, хотите ли вы получить весь результат (нужен он вам или нет) в одном запросе (активное получение) или вы хотите запросить его позже, когда вам это нужно (отложенное получение).

Используйте активную выборку, когда вам нужно получить небольшие данные одним выбором (один большой запрос). Или используйте ленивую выборку, чтобы запросить то, что вам нужно позже (много меньшего запроса).

использовать fetch, когда:

  • нет большой ненужной коллекции / набора внутри объекта, который вы собираетесь получить

  • связь между сервером приложений и сервером базы данных слишком велика и требует длительного времени

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

Angga
источник
Не могли бы вы объяснить это для запросов, которые я только что написал в обновленном вопросе.
Аббас
полезное соображение: «нет большой ненужной коллекции / набора внутри той сущности, которую вы собираетесь получить»
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

Обратите внимание, что SQL SELECT содержит только employeeстолбцы таблицы, а не departmentте. Чтобы получить departmentстолбцы таблицы, нам нужно использовать JOIN FETCHвместо JOIN.

ПРИСОЕДИНИТЬСЯ К FETCH

Таким образом, по сравнению с 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 выбираются столбцы таблицы, а не только те, которые связаны с сущностью, перечисленной в предложении FROMJPQL.

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

Влад Михалча
источник
Можно ли использовать несколько JOIN FETCH в одном запросе?
Онур Озкан
2
Вы можете ПРИСОЕДИНЯТЬСЯ, ВЫБРАТЬ несколько сопоставлений «многие к одному» и «один к одному» и не более одной коллекции. Извлечение нескольких коллекций, таких как ассоциации «один ко многим» или «многие ко многим», приведет к декартовому продукту . Однако, если вы хотите получить несколько коллекций, вы можете использовать вторичные запросы для второй, третьей, ..., n-й коллекций. Проверьте эту статью для более подробной информации.
Влад Михалча
5

Если у вас установлено @oneToOneсопоставление FetchType.LAZYи вы используете второй запрос (поскольку вам нужно, чтобы объекты Department загружались как часть объектов Employee), то, что будет делать Hibernate, будет выдавать запросы на выборку объектов Department для каждого отдельного объекта Employee, который он выбирает из БД.

Позже в коде вы можете получить доступ к объектам Department через однозначную ассоциацию Employee и Department, и Hibernate не будет выдавать никаких запросов для получения объекта Department для данного Employee.

Помните, что Hibernate по-прежнему выдает запросы, равные количеству выбранных сотрудников. Hibernate выдаст одинаковое количество запросов в обоих вышеуказанных запросах, если вы хотите получить доступ к объектам Department всех объектов Employee

Bunti
источник
2

Дерик: Я не уверен в том, что вы говорите, когда вы не используете fetch, результат будет иметь тип: List<Object[ ]>это означает список таблиц Object, а не список Employee.

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

Когда вы используете fetch, есть только один выбор, и в результате получается список Employee, List<Employee>содержащий список отделов. Он отменяет ленивую декларацию объекта.

Билал ВВВ
источник
Я не знаю, понимаю ли я вашу озабоченность. Если вы не используете fetch, ваш запрос будет возвращать только сотрудников. Если отделы, даже в этом случае, продолжают возвращаться, это потому, что ваше сопоставление между Employee и Department (a @OneToMany) установлено с помощью FetchType.EAGER. В этом случае любой HQL- fetchзапрос (с или без) FROM Employeeпринесет все отделы.
Дерик
Без использования fetch (только термин объединения) результатом будет массив коллекций, две строки, первая - коллекция сотрудников, а вторая - коллекция отделов. Используя нетерпеливую выборку или ленивую выборку, отделы будут извлечены.
Билал BBB
Без выборки на HQL это произойдет, только если ваше сопоставление между Employee и Department равно EAGER ( @OneToMany(fetch = FetchType.EAGER). Если это не так, Департаменты не будут возвращены.
Дерик
@ Дерик, попробуй сам, ты получишь исключение ClassCastException.
Билал BBB
Я выясняю проблему. Проблема не в извлечении, а в том, как это selectбыло сделано в HQL. Попробуй SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate имеют такое поведение возвращающие Listиз Object[]когда вы ommit на SELECTчасти.
Дерик