2011-04-28 29 views
34

考慮以下JPQL查詢:如何正確地將JPQL「join fetch」與「where」子句一起表示爲JPA 2 CriteriaQuery?

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

我想翻譯成Critieria查詢此。這是據我已經得到了:

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,查詢就像這麼簡單?

+1

嗯,謝謝,但我寧願堅持使用標準的API,並儘量避免額外的第三方庫。如果我想要做的事對JPA Criteria API來說是不可能的,那麼我可能會堅持使用普通的JPQL。 – chris 2011-04-28 09:40:05

+0

你解決了你的問題嗎?我有同樣的問題。 Fetch無法投射到Join,並且我無法從Fetch獲取路徑。它幾乎不可用。唯一的解決辦法是有兩個相同的連接,這是不可接受的。 – svlada 2015-04-22 07:17:16

+0

好吧,詹姆斯的回答很好地描述了問題的根源。你不能這樣做,這是一個健全的設計決策。如果我沒有記錯的話,我最終加入了兩次。這就是說,如果我有選擇,我將永遠不會再使用JPA,因爲我認爲這是一個無用的抽象層,在閹割底層實現時會增加不必要的複雜性。 – chris 2015-04-22 12:22:26

回答

52

在JPQL中,規範中實際上也是如此。 JPA規範不允許將別名賦予獲取聯接。問題在於,通過限制聯合抓取的上下文,您可以輕鬆地將自己拍攝在腳下。加入兩次更安全。

這通常是ToMany比ToOnes更多的問題。 例如,

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

這將錯誤返回包含在「613」區號電話號碼,但會漏掉其他地區的電話號碼,在返回的列表中的所有員工。這意味着在613和416區號中有電話的員工將丟失416電話號碼,因此該對象將被損壞。如果你知道你在做什麼,額外的連接是不可取的,一些JPA提供者可能允許取消連接的別名,並且可以允許將Criteria Fetch轉換爲Join。

+2

明智的答案,謝謝。沒有想到這一點。 Hibernate從來沒有向我抱怨關於別名加入的別名,我不知道這實際上違反了規範。 – chris 2011-04-28 14:17:57

+4

另請參閱http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html – James 2012-04-24 12:34:21