【问题标题】:JPA + Hibernate count(*) using CriteriaBuilder - with generatedAlias使用 CriteriaBuilder 的 JPA + Hibernate count(*) - 使用 generateAlias
【发布时间】:2012-12-31 01:55:41
【问题描述】:

当尝试使用CriteriaBuilder 创建count(*) 类型查询时,我遇到了以下别名问题。

我应该对下面的代码进行哪些更改才能获得计数?

约束:

  1. 我必须使用 CriteriaBuilder/Query,因为 where 子句必须根据值动态构建。
  2. 我只需要 COUNT,而不需要内存中的对象列表。

代码示例sn-p:

 Class<ReqStatCumulative> entityClass = ReqStatCumulative.class;
 @Override
    public long getCountForAlertConfig(AlertConfig cfg) {
        long count = 0L;
        if (null != cfg) {
            CriteriaBuilder qb = entityManager.getCriteriaBuilder();

            Metamodel model = entityManager.getMetamodel();
            EntityType<ReqStatCumulative> reqStatEntType_ = model.entity(entityClass);
            CriteriaQuery<ReqStatCumulative> cq = qb.createQuery(entityClass);
            Root<ReqStatCumulative> rootReqStatEnt = cq.from(reqStatEntType_);
            Path<Long> processTimeSeconds = rootReqStatEnt.<Long> get("processTimeSeconds");
            cq.where(qb.and(qb.greaterThan(processTimeSeconds, (long) cfg.getProcessTimeExceedsSec()),//
                    qb.lessThan(processTimeSeconds, (long) cfg.getProcessTimeExceedsSec() + 100))//
            );//
            findCountByCriteria(entityManager, cq, qb);
            log.debug("\n\t#####Alert desc:" + cfg.getDescription());
            log.debug("\n\t#####Alert count= " + count);
        } else {
            // Do nothing
        }
        return count;
    }

    public <T> Long findCountByCriteria(EntityManager em, CriteriaQuery<T> cqEntity, CriteriaBuilder qb) {
        CriteriaBuilder builder = qb;
        CriteriaQuery<Long> cqCount = builder.createQuery(Long.class);
        Root<?> entityRoot = cqCount.from(cqEntity.getResultType());
        cqCount.select(builder.count(entityRoot));
        cqCount.where(cqEntity.getRestriction());
        return em.createQuery(cqCount).getSingleResult();
    }

日志:我希望在所有 where 子句属性中使用 generateAlias0 而不是 generateAlias1。

select count(*) from abc.domain.ReqStatCumulative as **generatedAlias0** where ( **generatedAlias1**.processTimeSeconds>5L ) and ( **generatedAlias1**.processTimeSeconds<200L )

10:48:57.169 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - parse() - HQL: select count(*) from abc.domain.ReqStatCumulative as generatedAlias0 where ( generatedAlias1.processTimeSeconds>5L ) and ( generatedAlias1.processTimeSeconds<200L )
10:48:57.169 [main] DEBUG o.h.h.i.ast.QueryTranslatorImpl - --- HQL AST ---
 \-[QUERY] Node: 'query'
    +-[SELECT_FROM] Node: 'SELECT_FROM'
    |  +-[FROM] Node: 'from'
    |  |  \-[RANGE] Node: 'RANGE'
    |  |     +-[DOT] Node: '.'
    |  |     |  +-[DOT] Node: '.'
    |  |     |  |  +-[IDENT] Node: 'abc'
    |  |     |  |  \-[IDENT] Node: 'domain'
    |  |     |  \-[IDENT] Node: 'ReqStatCumulative'
    |  |     \-[ALIAS] Node: '**generatedAlias0**'
    |  \-[SELECT] Node: 'select'
    |     \-[COUNT] Node: 'count'
    |        \-[ROW_STAR] Node: '*'
    \-[WHERE] Node: 'where'
       \-[AND] Node: 'and'
          +-[GT] Node: '>'
          |  +-[DOT] Node: '.'
          |  |  +-[IDENT] Node: '**generatedAlias1**'
          |  |  \-[IDENT] Node: 'processTimeSeconds'
          |  \-[NUM_LONG] Node: '5L'
          \-[LT] Node: '<'
             +-[DOT] Node: '.'
             |  +-[IDENT] Node: '**generatedAlias1**'
             |  \-[IDENT] Node: 'processTimeSeconds'
             \-[NUM_LONG] Node: '200L'

10:48:57.169 [main] DEBUG o.h.hql.internal.ast.ErrorCounter - throwQueryException() : no errors
10:48:57.169 [main] DEBUG o.h.h.i.antlr.HqlSqlBaseWalker - select << begin [level=1, statement=select]
10:48:57.169 [main] DEBUG o.h.h.internal.ast.tree.FromElement - FromClause{level=1} : erf.domain.ReqStatCumulative (generatedAlias0) -> reqstatcum0_
10:48:57.169 [main] ERROR o.h.hql.internal.ast.ErrorCounter -  Invalid path: 'generatedAlias1.processTimeSeconds'
10:48:57.215 [main] ERROR o.h.hql.internal.ast.ErrorCounter -  Invalid path: 'generatedAlias1.processTimeSeconds'
org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'generatedAlias1.processTimeSeconds'

【问题讨论】:

    标签: hibernate jpa criteria-api


    【解决方案1】:

    您的代码失败,因为您对 count 和 where 子句使用了不同的 Root 实例:第一个(按定义顺序)生成 generatedAlias1 别名,另一个生成 generatedAlias0。您需要重构代码以便在两个地方使用相同的 Root 实例:

    CriteriaQuery<Long> cqCount = builder.createQuery(Long.class);
    Root<ReqStatCumulative> entityRoot = cqCount.from(cqEntity.getResultType());
    cqCount.select(builder.count(entityRoot));
    Path<Long> processTimeSeconds = entityRoot.get("processTimeSeconds");
    cqCount.where(qb.and(qb.greaterThan(processTimeSeconds, (long) cfg.getProcessTimeExceedsSec()),//
                    qb.lessThan(processTimeSeconds, (long) cfg.getProcessTimeExceedsSec() + 100))//
                    );//    
    return em.createQuery(cqCount).getSingleResult();
    

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题,我解决了:

      CriteriaQuery<Long> countCriteria = cb.createQuery(Long.class);
      Root<EntityA> countRoot = countCriteria.from(cq.getResultType());
      Set<Join<EntityA, ?>> joins = originalEntityRoot.getJoins();
      for (Join<EntityA, ?> join :  joins) {
          countRoot.join(join.getAttribute().getName());
      }
      countCriteria.select(cb.count(countRoot));
      if(finalPredicate != null)
          countCriteria.where(finalPredicate);
      
      TypedQuery<Long> queryCount = entityManager.createQuery(countCriteria);
      Long count = queryCount.getSingleResult();
      

      在哪里

      originalEntityRoot 是我使用 where 子句进行查询的主根。

      【讨论】:

      • 这种方法很好,但是如果这些连接中的任何一个依次具有连接,则这些连接将不会被复制.. 可能必须执行一些递归操作。
      【解决方案3】:

      我正在寻找基于 OP 问题的更通用的解决方案。 并且只留下一个基于@Hector 示例的更通用的解决方案:

      public class CountQueryHelper<T> {
      
          final Class<T> typeParameterClass;
      
          public CountQueryHelper(Class<T> typeParameterClass) {
              this.typeParameterClass = typeParameterClass;
          }
      
          public CriteriaQuery<Long> getCountQuery(CriteriaQuery<T> originalQuery, EntityManager em) {
              CriteriaBuilder cb = em.getCriteriaBuilder();
      
              // create count query
              CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
      
      
              // start copying root/joins/restrictions from  the original query
      
              // copy roots
              for (Root r : originalQuery.getRoots()) {
                  Root root = countQuery.from(r.getModel());
                  root.alias(r.getAlias());
              }
      
              // copy joins
              for (Root r : originalQuery.getRoots()) {
                  Set<Join<T, ?>> joins = r.getJoins();
                  for (Join<T, ?> join : joins) {
                      for (Root countRoot : countQuery.getRoots()) {
                          try {
                              Join joinOnCount = countRoot.join(join.getAttribute().getName());
                              joinRecursive(joinOnCount, join);
                          } catch (IllegalArgumentException e) {
                              // attribute does not exist on this root
                          }
                      }
                  }
              }
      
              countQuery.select(cb.count(countQuery.from(this.typeParameterClass)));
      
              //  copy restrictions
              if (originalQuery.getRestriction() != null) {
                  countQuery.where(originalQuery.getRestriction());
              }
      
              return countQuery;
          }
      
          private void joinRecursive(Join countJoins, Join<T, ?> originalJoin) {
              for(Join original : originalJoin.getJoins()) {
                  Join<Object, Object> childJoin = countJoins.join(original.getAttribute().getName());
                  joinRecursive(childJoin, original);
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-04-10
        • 1970-01-01
        • 2012-02-29
        • 2014-01-01
        • 2011-06-05
        • 2019-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多