简体   繁体   English

如何正确地将带有“where”子句的 JPQL“join fetch”表示为 JPA 2 CriteriaQuery?

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

Consider the following JPQL query:考虑以下 JPQL 查询:

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

I'm trying to translate this into a Criteria query.我正在尝试将其转换为 Criteria 查询。 This is as far as I have gotten:据我所知,这是:

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);

The obvious problem here is that I am doing the same join twice, because Fetch<Foo, Bar> doesn't seem to have a method to get a Path .这里明显的问题是我做了两次相同的连接,因为Fetch<Foo, Bar>似乎没有获取Path的方法。 Is there any way to avoid having to join twice?有什么办法可以避免两次加入吗? Or do I have to stick with good old JPQL with a query as simple as that?还是我必须坚持使用像这样简单的查询的旧 JPQL?

In JPQL the same is actually true in the spec.在 JPQL 中,规范中实际上也是如此。 The JPA spec does not allow an alias to be given to a fetch join. JPA 规范不允许为获取连接指定别名。 The issue is that you can easily shoot yourself in the foot with this by restricting the context of the join fetch.问题是您可以通过限制连接获取的上下文轻松地用这个来打自己的脚。 It is safer to join twice.加入两次更安全。

This is normally more an issue with ToMany than ToOnes.这通常是 ToMany 的问题,而不是 ToOnes。 For example,例如,

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

This will incorrectly return all Employees that contain numbers in the '613' area code but will left out phone numbers of other areas in the returned list.这将错误地返回所有在“613”区号中包含数字的员工,但会在返回的列表中遗漏其他地区的电话号码。 This means that an employee that had a phone in the 613 and 416 area codes will loose the 416 phone number, so the object will be corrupted.这意味着拥有 613 和 416 区号电话的员工将丢失 416 电话号码,因此 object 将被损坏。

Granted, if you know what you are doing, the extra join is not desirable, some JPA providers may allow aliasing the join fetch, and may allow casting the Criteria Fetch to a Join.当然,如果您知道自己在做什么,那么额外的连接是不可取的,一些 JPA 提供程序可能允许别名连接提取,并且可能允许将标准提取转换为连接。

I will show visually the problem, using the great example from James answer and adding the alternative solution.我将使用 James answer 中的出色示例并添加替代解决方案来直观地展示问题。

When you do the follow query, without the FETCH :当您执行以下查询时,没有FETCH

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

You will have the follow results from Employee as you expected:如您所料,您将获得来自Employee的以下结果:

EmployeeId员工ID EmployeeName员工姓名 PhoneId电话号码 PhoneAreaCode电话区号
1 1 James詹姆士 5 5 613 613
1 1 James詹姆士 6 6 416 416

But when you add the FETCH word on JOIN , this is what happens:但是当您在JOIN上添加FETCH字时,会发生以下情况:

EmployeeId员工ID EmployeeName员工姓名 PhoneId电话号码 PhoneAreaCode电话区号
1 1 James詹姆士 5 5 613 613

The generated SQL is the same for the two queries, but the Hibernate removes on memory the 416 register when you use WHERE on the FETCH join.生成的 SQL 对于两个查询是相同的,但是 Hibernate 删除了 memory 上416寄存器,当您在FETCH上使用WHERE时。

So, to bring all phones and apply the WHERE correctly, you need to have two JOIN s: one for the WHERE and another for the FETCH .因此,要带上所有电话正确应用WHERE ,您需要有两个JOIN :一个用于WHERE ,另一个用于FETCH Like:喜欢:

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

I may answer late this but from my point of view.我可能会迟到回答这个问题,但从我的角度来看。

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

This could be translated to the following SQL Query这可以翻译为以下 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'  
)

This will get all phones of an employee that belongs to an area.这将获取属于某个区域的员工的所有电话。

BUT

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

could be translated to the following SQL Queries可以翻译成下面的 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'  

or或者

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'  

this will restrict row selection to only rows where employees phone is of area XXX这会将行选择限制为仅员工电话位于 XXX 区域的行

And finally writing this最后写这个

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

Could be seen as可以看作

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'
)

Where we are only getting employee data that have a phone number in some area我们只获取在某些区域有电话号码的员工数据

This should help get the idea after each query.这应该有助于在每次查询后得到这个想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM