考虑以下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,使用这么简单的查询?
3条答案
按热度按时间ntjbwcob1#
在JPQL中,规范中也是如此。JPA规范不允许为获取连接提供别名。问题是,通过限制连接获取的上下文,你很容易用这个来搬起石头砸自己的脚。连接两次更安全。
这通常是ToMany比ToOnes更大的问题。例如,
这将不正确地返回所有包含区号为“613”的员工,但在返回的列表中会遗漏其他地区的电话号码。这意味着在613和416区号中拥有电话的员工将丢失416电话号码,因此对象将被损坏。
当然,如果您知道自己在做什么,那么额外的联接是不可取的,一些JPA提供程序可能允许对联接获取进行别名化,并且可能允许将Criteria Fetch强制转换为一个Join。
epfja78i2#
我将使用James answer中的相同示例并添加替代解决方案来直观地展示问题。
当您执行以下查询时,不使用
FETCH
:您将从
Employee
获得以下结果,正如您所期望的:| 员工ID|员工姓名|电话ID|电话区域代码|
| --------------|--------------|--------------|--------------|
| 1|詹姆斯|五|六一三|
| 1|詹姆斯|六|四一六|
但是,当您在
JOIN
(FETCH JOIN
)上添加FETCH
子句时,会发生以下情况:| 员工ID|员工姓名|电话ID|电话区域代码|
| --------------|--------------|--------------|--------------|
| 1|詹姆斯|五|六一三|
这两个查询生成的SQL是相同的,但是当您在
FETCH
连接中使用WHERE
时,Hibernate会删除内存中的416
寄存器。因此,要使所有电话和正确应用
WHERE
,您需要有两个JOIN
:一个用于WHERE
,另一个用于FETCH
。例如:也许在最新版本的Hibernate中,你需要使用
SELECT DISTINCT
来避免重复的结果。ds97pgxw3#
我可能会晚些时候回答这个问题,但从我的Angular 来看。
这可以转换为以下SQL查询
这将获取属于某个区域的员工的所有电话。
但是
可以转换为以下SQL查询
或
这将把行选择限制为只有员工电话属于XXX区域的行
最后写了这个
可以被看作是
在那里我们只得到员工的数据,有一个电话号码,在某些地区
这应该有助于在每次查询后获得想法。