JPQL 中可以选择 EXISTS() 吗?
我正在尝试运行一个查询来检查某些条件是否为真并返回一个简单的布尔结果作为输出。有点棘手的是,其中一个条件是测试一组条件是否没有返回结果。
我目前正在使用 JPA-2.0 和 hibernate 作为我的提供程序,由 MySQL 支持。我已经得到了一个在 MySQL 中运行良好的示例查询,但是当尝试让它在 JPQL 中运行时,它失败了。 MySQL 查询看起来有点像这样:
Select exists(Select statement with criteria)
or not exists(Select statement with criteria);
我也使用 CASE 得到了相同的输出,但因为 JPQL 不支持该语句。
无论如何,当我尝试在 JPQL 中使用类似的查询时,我收到错误:
“子树意外结束”
,根据我的理解,这意味着查询中缺少某些内容。有谁知道如何解决它?
I'm trying to run a query that checks if some conditions are true and returns a simple boolean result as output. What makes it slightly tricky is that one of the conditions is to test for whether no results are returned for a set of criteria.
I'm currently using JPA-2.0 with hibernate as my provider, backed by MySQL. I have gotten an example query working fine in MySQL, but when trying to get it running in JPQL it flops. The MySQL query looks a bit like this:
Select exists(Select statement with criteria)
or not exists(Select statement with criteria);
I also got the same output using CASE, but as JPQL doesn't support that statement.
Anyways, when I try to use a similar query in JPQL I get the error:
"unexpected end of subtree"
which from my understanding means that something is missing in the query. Does anyone have any idea how to fix it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
DB 不统计所有记录的效率更高。创建本机查询
Spring-Data-JPA
或 JPA:
It is more efficient for DB not counting all records. Create native query
Spring-Data-JPA
or JPA:
不幸的是,
exists
表达式(在 JPA 2.0 中添加)只能在where
子句中使用。所以你不能直接按照你想要的方式使用它。但是,通过稍微修改语法,您至少可以在一些更简单的用例中使用它。基本思想是,而不是
选择存在(选择符合条件的内容)
你写
选择存在的常量(选择条件所在的内容)
,结果由您是否获得(常量)结果行的事实给出。对于常量,我不确定是否有比包含 PK 从任何表中选择的(基本上是虚拟的)更简单的方法。作为更具体的示例,假设您要检查数据库中是否存在具有指定电子邮件地址的第二个人。你想写一些类似的东西
选择存在(选择 p.id WHERE p.phoneNumber=:phoneNumber 和 p.id<>:primaryPersonId)
。其他答案让你写
select count(p.id) where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
(或稍微调整但对性能没有明显影响的select (count 时的情况) (p.id)>0 then 1 else 0) 其中 p.phoneNumber=:phoneNumber 且 p.id<>:primaryPersonId)
)它工作正常,但不必要地计算完整计数,这可能会对数据库产生性能影响(取决于具体情况)。
但你可以写
从 p.id=:primaryPersonId 且存在的人员中选择 p.id(select p.id where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
当有重复项时,它将返回一个结果行;当没有重复项时(当
exists
返回false
时),不返回任何行。Unfortunately, the
exists
expression (added in JPA 2.0) can be used only in thewhere
clause. So you cannot use it directly in the way you want. However, with a slight massaging of syntax, you can use it in some simpler usecases, at least.The basic idea is that instead of
select exists(select something where criteria)
you write
select constant where exists(select something where criteria)
and the result is given by the fact whether you got the (constant) result row or not. For the constant, I’m not sure if there is a simpler way than to include a (basically dummy) select by PK from any table.As a more specific example, imagine you want to check whether there is a second person with the specified e-mail address in the database. You’d like to write something similar to
select exists(select p.id WHERE p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
.The other answers make you write
select count(p.id) where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
(or the slightly tweaked but without noticeable effect on performanceselect (case when count(p.id)>0 then 1 else 0) where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
)which works fine but unnecessarily computes the full count which can have performance impact on the database (depending on the specifics).
But you can write
select p.id from persons where p.id=:primaryPersonId and exists(select p.id where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
instead,which will return one result row when there are duplicates, and returns no rows when there are no duplicates (when the
exists
returnedfalse
).解决方案
count(*) > 0
对我来说在 Postgres 中触发了全表扫描:正确的性能方法是使用
limit
或top
关键字进行短路扫描。由于分页不可移植,您必须求助于setMaxResults()
:The solution with
count(*) > 0
triggered full table scan in Postgres for me:The proper performant way is to short-circuit scan with
limit
ortop
keyword. As pagination is not portable you have to resort tosetMaxResults()
:您可以使用 case 表达式< /a>.
从 JPA 2.0 (Java EE 6) 开始,您可以 创建一个 TypedQuery 。
在 JPA 1.0 (Java EE 5) 中,您必须使用非类型化查询。
You can do a boolean query using a case expression.
As of JPA 2.0 (Java EE 6) you can create a TypedQuery .
In JPA 1.0 (Java EE 5) you must use an untyped query.
这个答案已经过时了。是的,这是可能的。请参考 Rene Link 的正确答案
不,这是不可能的。
请参阅 JPQL BNF 文档来自甲骨文。
This answer is obsolete. Yes, it is possible. Please refer to the correct answer from Rene Link
No, it is not possible.
Refer to the JPQL BNF documentation from oracle.
在一个使用 Hibernate 5.2(支持 JPA 2.1)和 Spring Data Jpa 2.0.6 的项目中,我成功地使用了这个 JPQL 查询:
在日志中,我读到生成的本机查询如下:
In a project with Hibernate 5.2 (which supports JPA 2.1), and Spring Data Jpa 2.0.6, I successfully used this JPQL query:
In the logs, I read that the produced native query is the following:
您的括号不匹配。尝试删除
not
之前的那个(以及第一个存在周围的):您不需要将
exists()
括起来的括号You have mismatched brackets. Try removing the one before the
not
(and the ones around the first exists):You don't need brackets around
exists()
或者,您可以使用
select count(...)
并测试它是否返回0
。这应该几乎同样高效,而不需要编写更多代码(事实上,查询本身可能看起来更简单)。Alternatively you could use a
select count(...)
and test whether it returns0
. This should be almost as efficient without requiring to write much more code (in fact, the query itself will probably look simpler).在一个使用的项目中,
我成功地使用了一个
子句。所以它可能已经改变了。它也可能是 Hibernate 专有的。由于很多人使用 Hibernate 作为持久性提供程序,我想我可以在这里添加它。
In a project that uses
I successfully used an
clause. So it might have changed. It could also be Hibernate-proprietary. Since a lot of people use Hibernate as a persistence provider, I thought I might add this here.