次のJPQLクエリを検討してください。
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);
ここでの明らかな問題は、同じ結合を2回実行していることです。なぜならFetch<Foo, Bar>
にはPath
を取得するメソッドがないようです。 2回参加することを避ける方法はありますか?それとも、それと同じくらい簡単なクエリで古き良きJPQLに固執する必要がありますか?
JPQLでは、仕様でも同じことが実際に当てはまります。 JPA仕様では、フェッチ結合にエイリアスを与えることは許可されていません。問題は、結合フェッチのコンテキストを制限することで、これで簡単に自分自身を撃つことができることです。 2回参加する方が安全です。
これは通常、ToOneよりもToManyの問題です。例えば、
Select e from Employee e
join fetch e.phones p
where p.areaCode = '613'
これは、誤って '613'市外局番に番号を含むすべての従業員を返しますが、返されたリストの他の地域の電話番号は除外します。これは、613と416の市外局番に電話を持っていた従業員が416の電話番号を失うため、オブジェクトが破損することを意味します。
確かに、あなたが何をしているのか知っているなら、余分な結合は望ましくありません。一部のJPAプロバイダーは結合フェッチのエイリアスを許可し、Criteria Fetchを結合にキャストできる場合があります。
ジェームズの回答の素晴らしい例を使用し、代替ソリューションを追加して、視覚的に問題を示します。
FETCH
なしで次のクエリを実行すると:
Select e from Employee e
join e.phones p
where p.areaCode = '613'
Employee
から期待どおり次の結果が得られます。
EmployeeId | EmployeeName | PhoneId | PhoneAreaCode
1 | James | 5 | 613
1 | James | 6 | 416
ただし、FETCH
にJOIN
Wordを追加すると、次のようになります。
EmployeeId | EmployeeName | PhoneId | PhoneAreaCode
1 | James | 5 | 613
生成されたSQLは2つのクエリで同じですが、Hibernateはメモリ上 the 416
WHERE
結合でFETCH
を使用するときに登録します。
したがって、すべての電話を持ち込むには、およびWHERE
を正しく適用するには、JOIN
用とWHERE
用の2つのFETCH
が必要です。 [$ var] _。好む:
Select e from Employee e
join e.phones p
join fetch e.phones //no alias, to not commit the mistake
where p.areaCode = '613'