【问题标题】:EclipseLink JPQL: Generic COUNT statement for any statementEclipseLink JPQL:任何语句的通用 COUNT 语句
【发布时间】:2018-03-29 23:39:11
【问题描述】:

我有一个应用程序,它遇到了用JPQL 编写的非常不同的查询(在不同的资源上)。

对于很多此类查询,我需要知道结果总数(计数),因为我没有应用任何 LIMIT/OFFSET

因为这个查询的性质非常不同,我无法构建一个解析器来提取FROM 子句,它适用于SELECT COUNT 查询。

我们来看一个例子:

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

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

尝试使用标准模式,计数查询将是 SELECT (COUNT) z FROM (...) z 并在缺少的部分中,我会将完整的查询作为 2 before

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

从文档中我可以看到支持 FROM 子句中的子选择:https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause

这是我制作的示例代码

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

但如果我尝试执行查询(我正在使用 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)

即使似乎支持子选择,cannot be the first declaration of the FROM clause. 错误也很明显。

我做错了吗?

我的最终目标是尝试有一个标准的COUNT 查询,我可以将它用于任何类型的语句,只需使用COUNT(*) FROM (..statement..),这样实现非常通用。

或者,如果有一些方法可以实现相同的目标,那也是有用的。


编辑

还尝试使用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.

【问题讨论】:

  • 您能否解释一下为什么查询结果列表的大小不够好? docs.oracle.com/javaee/6/api/javax/persistence/…
  • 只是一个镜头但count(0)没有z别名?
  • @pirho 尝试过,但我仍然收到错误消息(请参阅主帖上的编辑)
  • @ScaryWombat 有两个原因:1-我想知道结果的总数,除了最终应用于查询的限制和偏移。 2- 在没有限制/偏移的查询上点击 getResultsList 可以检索很多(数千)条记录,而我只需要知道计数值
  • @DevilingMaster 是的,认为 jpql 无论如何都需要指向字段,所以如果 z 否则会起作用,猜测它应该是 z.id 左右......

标签: java jpa count eclipselink


【解决方案1】:

根据@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));
    }

我尝试了几个查询(简单查询,连接查询,...),它似乎工作得很好。 此外,当生成 SQL 字符串时,最终应用于查询对象的 LIMIT 或 OFFSET 将被忽略,因此我什至不需要从生成的语句中删除它。

queryToSqlString 也可以非常方便地记录提交给 DBMS 的本机语句(如果您不想启用完整的 EclipseLink 日志记录)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    • 2011-06-23
    • 2016-05-21
    • 1970-01-01
    • 2014-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多