簡體   English   中英

如何正確地將帶有“where”子句的 JPQL“join fetch”表示為 JPA 2 CriteriaQuery?

[英]How to properly express JPQL “join fetch” with “where” clause as JPA 2 CriteriaQuery?

考慮以下 JPQL 查詢:

SELECT foo FROM Foo foo
INNER JOIN FETCH foo.bar bar
WHERE bar.baz = :baz

我正在嘗試將其轉換為 Criteria 查詢。 據我所知,這是:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
Root<Foo> r = cq.from(Foo.class);
Fetch<Foo, Bar> fetch = r.fetch(Foo_.bar, JoinType.INNER);
Join<Foo, Bar> join = r.join(Foo_.bar, JoinType.INNER);
cq.where(cb.equal(join.get(Bar_.baz), value);

這里明顯的問題是我做了兩次相同的連接,因為Fetch<Foo, Bar>似乎沒有獲取Path的方法。 有什么辦法可以避免兩次加入嗎? 還是我必須堅持使用像這樣簡單的查詢的舊 JPQL?

在 JPQL 中,規范中實際上也是如此。 JPA 規范不允許為獲取連接指定別名。 問題是您可以通過限制連接獲取的上下文輕松地用這個來打自己的腳。 加入兩次更安全。

這通常是 ToMany 的問題,而不是 ToOnes。 例如,

Select e from Employee e 
join fetch e.phones p 
where p.areaCode = '613'

這將錯誤地返回所有在“613”區號中包含數字的員工,但會在返回的列表中遺漏其他地區的電話號碼。 這意味着擁有 613 和 416 區號電話的員工將丟失 416 電話號碼,因此 object 將被損壞。

當然,如果您知道自己在做什么,那么額外的連接是不可取的,一些 JPA 提供程序可能允許別名連接提取,並且可能允許將標准提取轉換為連接。

我將使用 James answer 中的出色示例並添加替代解決方案來直觀地展示問題。

當您執行以下查詢時,沒有FETCH

Select e from Employee e 
join e.phones p 
where p.areaCode = '613'

如您所料,您將獲得來自Employee的以下結果:

員工ID 員工姓名 電話號碼 電話區號
1 詹姆士 5 613
1 詹姆士 6 416

但是當您在JOIN上添加FETCH字時,會發生以下情況:

員工ID 員工姓名 電話號碼 電話區號
1 詹姆士 5 613

生成的 SQL 對於兩個查詢是相同的,但是 Hibernate 刪除了 memory 上416寄存器,當您在FETCH上使用WHERE時。

因此,要帶上所有電話正確應用WHERE ,您需要有兩個JOIN :一個用於WHERE ,另一個用於FETCH 喜歡:

Select e from Employee e 
join e.phones p 
join fetch e.phones      //no alias, to not commit the mistake
where p.areaCode = '613'

我可能會遲到回答這個問題,但從我的角度來看。

Select e from Employee e 
join e.phones p 
join fetch e.phones      //no alias, to not commit the mistake
where p.areaCode = '613'

這可以翻譯為以下 SQL 查詢

Select e.id, e.name, p.id ,p.phone
From Employe e
inner join Phone p on e.id = p.emp_id
where exists(
  select 1 from Phone where Phone.id= p.id and Phone.area ='XXX'  
)

這將獲取屬於某個區域的員工的所有電話。

Select e from Employee e 
join fetch e.phones p      //no alias, to not commit the mistake
where p.areaCode = '613'

可以翻譯成下面的 SQL 查詢

Select  e.id, e.name, p.id ,p.phone
From    Employe e
inner   join Phone p on e.id = p.id
Where   p.area ='XXX'  

或者

Select e.id, e.name, p.id ,p.phone
From Employe e
inner join Phone p on e.id = p.emp_id and p.area ='XXX'  

這會將行選擇限制為僅員工電話位於 XXX 區域的行

最后寫這個

Select e from Employee e 
join  e.phones p      
where p.areaCode = '613'

可以看作

Select e.id, e.name 
from Employe e
where exists (
 select 1 from phone p where p.emp_id = e.id and p.area = 'XXX'
)

我們只獲取在某些區域有電話號碼的員工數據

這應該有助於在每次查詢后得到這個想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM