問題描述
正如我們上面所說, LazyCollectionOption.EXTRA註解有一個問題——它為每個對像對數據庫執行單獨的請求。我們需要以某種方式向 Hibernate 解釋我們希望它立即為我們的父對象加載所有子對象。
Hibernate 的開發者想出了解決這個問題的方法, HQL 中的join fetch operator 。
HQL查詢示例:
select distinct task from Task t left join fetch t.employee order by t.deadline
在這個查詢中,一切都既簡單又復雜。讓我們嘗試一點一點地構建它。
選項1
我們要下載所有對象任務, 按截止日期排序。該請求如下所示:
select task from Task t order by t.deadline
雖然一切都很清楚。但是場員工Task類的 將包含一個用EXTRA註釋註釋的 Employees 集合。並且不會加載此集合的對象。
選項 2
強制Hibernate為一個對象加載子對象任務.
select task from Task t join fetch t.employee order by t.deadline
在幫助下,我們在查詢中明確綁定了 Task 和 Employee 實體。Hibernate 已經知道這一點,因為我們在這些字段上使用了@ManyToMany註釋。
但是我們需要一個join語句來完成它,用一個fetch語句來獲取一個 join fetch。這就是我們如何告訴 Hibernate 在執行我們的請求時需要從數據庫中加載 Task.employee 集合中的對象。
選項 3
以前的解決方案有一些錯誤。一、使用join後,SQL不會返回對像給我們任務,在 Employee 表中沒有與之關聯的對象。這正是inner join 的工作原理。
所以我們需要用左運算符來擴充我們的連接,並將其變成左連接。例子:
select task from Task t left join fetch t.employee order by t.deadline
選項 4
但這還不是全部。如果在你的代碼中,實體之間的關係是多對多的,那麼查詢結果中就會出現重複。同一個對象任務可以在不同的員工(員工對象)上找到。
因此,您需要在選擇詞之後添加 distinct 關鍵字以擺脫重複的 Task 對象。
select distinct task from Task t left join fetch t.employee order by t.deadline
這就是我們如何在 4 個步驟中達到我們開始的請求。好吧,Java 代碼看起來很符合預期:
String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
return query.list();
JOIN FETCH 限制
沒有人是完美的。JOIN FETCH 語句也是如此。它有很多限制。第一個是使用setMaxResults()和setFirstResult()方法。
對於 JOIN FETCH 語句,我們的 Hibernate 將生成一個非常複雜的查詢,其中我們將三個表合併為一個:employee、task 和 employee_task。事實上,這不是對員工或任務的請求,而是對所有已知的員工-任務對的請求。
SQL 可以將其 LIMIT 和 OFFSET 語句準確地應用於員工-任務對的查詢。同時,從HQL查詢中可以清楚地看出,我們要準確獲取任務(Task),如果我們重新分配我們的FirstResult和MaxResult參數,那麼它們應該具體指代Task對象。
如果你寫這樣的代碼:
String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
query.setFirstResult(0);
query.setMaxResults(1);
return query.list();
那麼 Hibernate 將無法正確地將 FirstResult 和 MaxResult 轉換為 SQL 查詢的 OFFSET 和 LIMIT 參數。
相反,它會做三件事:
- SQL 查詢通常會從表中選擇所有數據並將其返回給 Hibernate
- Hibernate 會在它的內存中選擇必要的記錄並返回給你
- Hibernate 會發出警告
警告將是這樣的:
WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104:
firstResult/maxResults specified with collection fetch; applying in memory!
GO TO FULL VERSION