简体   繁体   English

EclipseLink JPQL:任何语句的通用COUNT语句

[英]EclipseLink JPQL: Generic COUNT statement for any statement

I have an application which hit very different queries (on different resources) written in JPQL . 我有一个应用程序,它可以用JPQL编写非常不同的查询(在不同的资源上)。

For a lot of this queries I need to know the total of results (count) as I'm not applying any LIMIT / OFFSET 对于很多此类查询,我需要知道结果总数(计数),因为我没有应用任何LIMIT / OFFSET

Because the nature of this queries is very different, I'm unable to build a parser which extract the FROM clause and it applies on a SELECT COUNT query. 由于此查询的性质非常不同,因此无法构建解析器来提取FROM子句,并且该解析器适用于SELECT COUNT查询。

Let's see an example: 让我们来看一个例子:

  • Query 1: SELECT a FROM People a WHERE name = 'John' 查询1: SELECT a FROM People a WHERE name = 'John'

  • Query 2: SELECT DISTINCT(o.category.id) FROM Company o 查询2: SELECT DISTINCT(o.category.id) FROM Company o

Trying to use a standard pattern, a count query will be SELECT (COUNT) z FROM (...) z and inside the missing parte I would put the full query as the 2 before 尝试使用标准模式,计数查询将是SELECT(COUNT)z FROM(...)z,在缺少的部分内,我会将完整查询作为2

Query 1: SELECT COUNT(z) FROM (SELECT a FROM People a WHERE name = 'John') z Query 2: SELECT COUNT(z) FROM (SELECT DISTINCT(o.category.id) FROM Company o) z 查询1:从SELECT COUNT(z) FROM (SELECT a FROM People a WHERE name = 'John') z查询2:从SELECT COUNT(z) FROM (SELECT DISTINCT(o.category.id) FROM Company o) z

From the documentation I can see that Sub-selects in FROM clause are supported: https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause 从文档中,我可以看到支持FROM子句中的子选择: https : //wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause

This is the sample code I made 这是我编写的示例代码

try {
            String subSelect = "SELECT a FROM People a WHERE name = 'John'";
            String statement = "SELECT COUNT(z) FROM (" + subSelect + ") z";
            TypedQuery<Long> createQuery = em.createQuery(statement, Long.class);
            System.out.println(createQuery.getSingleResult().longValue());
        } finally {
            em.close();
        }

But if I try to execute the query (I'm using EclipseLink 2.7.0 ) I'm getting this error: 但是,如果我尝试执行查询(我正在使用EclipseLink 2.7.0 ),则会收到此错误:

java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager: 
Exception Description: Problem compiling [SELECT COUNT(z) FROM (SELECT a FROM People a WHERE name = 'John') z]. 
[21, 67] '(SELECT a FROM People a WHERE name = 'John') z' cannot be the first declaration of the FROM clause.
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1743)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1764)

Even if sub-select seems to be supported, the cannot be the first declaration of the FROM clause. 即使似乎支持子选择, cannot be the first declaration of the FROM clause. error is clear. 错误很明显。

Am I doing something wrong? 难道我做错了什么?

The end goal of mine is trying to have a standard COUNT query which I can use for any kind of statement, simplying using COUNT(*) FROM (..statement..) , in this way the implementation is very generic. 我的最终目标是尝试有一个标准的COUNT查询,我可以将其用于任何类型的语句,只需使用COUNT(*) FROM (..statement..) ,这样实现非常通用。

Or, if there are methods which can allow to achieve the same goal, can be useful as well. 或者,如果有可以实现相同目标的方法,则也很有用。


EDIT 编辑

Also tried with COUNT(0)' as suggested, like this: SELECT COUNT(0) FROM (SELECT DISTINCT(o.category.id) FROM Company o)` 还尝试COUNT(0)' as suggested, like this:使用COUNT(0)' as suggested, like this: SELECT COUNT(0)FROM(SELECT DISTINCT(o.category.id)FROM Company o)`

Exception Description: Syntax error parsing [SELECT COUNT(0) FROM (SELECT DISTINCT(o.category.id) FROM Company o)]. 
[68, 68] An identification variable must be provided for a range variable declaration.

With suggestion from @chris I came up with the following solution 在@chris的建议下,我提出了以下解决方案

    /**
     * https://stackoverflow.com/a/43933889/1013317
     */
    private static String queryToSqlString(EntityManager em, Query query) {
        Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
        DatabaseQuery databaseQuery = query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
        databaseQuery.prepareCall(session, new DatabaseRecord());
        Record r = databaseQuery.getTranslationRow();

        // Query with parameters
        return databaseQuery.getTranslatedSQLString(session, r);
    }

    /**
     * Overload, see {@link #queryToSqlString(EntityManager, Query)}
     */
    private static String queryToSqlString(EntityManager em, String statement) {
        return queryToSqlString(em, em.createQuery(statement));
    }

    /**
     * Count the amount of results for a given statement
     */
    private static long count(EntityManager em, Query query) {
        String countStatement = String.format("SELECT COUNT(*) FROM (%s) AS t", queryToSqlString(em, query));
        Query createNativeQuery = em.createNativeQuery(countStatement);
        return (long) createNativeQuery.getSingleResult();
    }

    /**
     * Overload, see {@link #count(EntityManager, Query)}
     */
    private static long count(EntityManager em, String statement) {
        return count(em, em.createQuery(statement));
    }

I tried with a couple of queries (simple queries, join queries, ...) and it seems to works very fine. 我尝试了几个查询(简单查询,联接查询等),它似乎工作得很好。 Also, eventual LIMIT or OFFSET applied to the query object are ignored when SQL string is generated, so I don't even need to remove that from generated statement. 另外,生成SQL字符串时,最终应用于查询对象的LIMIT或OFFSET会被忽略,因此我什至不需要从生成的语句中删除它。

The queryToSqlString can also be very handy to log native statement that are submitted to the DBMS (if you do not want to enable the full EclipseLink logging) queryToSqlString还可以非常方便地记录提交给DBMS的本机语句(如果您不想启用完整的EclipseLink记录)

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

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