繁体   English   中英

使用休眠供应商的计数 JPA 2.0 包装条件查询

[英]Wrap criteria query with count JPA 2.0 with hibernate vendor

有没有办法用计数查询包装 CriteriaQuery? 我的想法是创建一个从任何给定查询创建计数查询的函数。

例如在平面 sql 中:

SELECT
  item_type,
  count(*) AS lol
FROM inventory_movements
WHERE movement_date_time BETWEEN '2017-05-08 12:00:00' AND '2017-05-08 13:00:00'
GROUP BY item_type

我想创建这样的东西(我知道在这个特定的查询中一个 count distinct 可以解决这个问题,但我需要一个通用的解决方案,我想用它在非 jpa 托管返回类型查询中进行分页):

SELECT count(*)
FROM (
       SELECT
         item_type,
         count(*) AS rand_
       FROM inventory_movements
       WHERE movement_date_time BETWEEN '2017-05-08 12:00:00' AND '2017-05-08 13:00:00'
       GROUP BY item_type
     ) AS sub;

CriteriaQuery 不是 Expression 的实例,所以它不能在 criteriaBuilder.count() 中使用;

然后我想我可以将它转换为子查询,因为 CriteriaQuery 和子查询都实现了相同的接口,但子查询也实现了表达式。

但这并不像我想的那样工作。 这是我目前的非工作代码:

public <T>TypedQuery<Long> getCountQuery(CriteriaQuery<T> origQuery) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Long> query = cb.createQuery(Long.class);
    Subquery<T> subQuery = (Subquery<T>) origQuery;

    query.select(cb.count(subQuery));
    return entityManager.createQuery(query);
}

我在运行时收到以下异常:

java.lang.ClassCastException: org.hibernate.query.criteria.internal.CriteriaQueryImpl 不能转换为 javax.persistence.criteria.Subquery

这在 jpa/hibernate 中甚至可能吗?

@PersistenceContext
private EntityManager em;

...
...
public Page<T1> findByParameters(P p) {
        CriteriaQuery<T1> criteriaQuery = (CriteriaQuery<T1>) getEm().getCriteriaBuilder().createQuery(typeToken1.getRawType());           
        Root<T2> root = (Root<T2>) criteriaQuery.from(typeToken2.getRawType());
        criteriaQuery = SpecificationBuilder.of().buildCriteriaQuery(p, root, criteriaQuery, criteriaBuilder);

        TypedQuery<T1> query = getEm().createQuery(criteriaQuery);
        query.setFirstResult(pageRequest.getPageNumber() * pageRequest.getPageSize());
        query.setMaxResults(pageRequest.getPageSize());

        Integer totoal = getTotal(criteriaQuery, (CriteriaQueryTypeQueryAdapter) query);
        PageImpl<T1> page1 = new PageImpl<>(query.getResultList(), pageRequest, totoal);
         return page1;
}

private Integer getTotal(CriteriaQuery<T1> criteriaQuery, CriteriaQueryTypeQueryAdapter query) throws NoSuchFieldException, IllegalAccessException {
        String jql = getEm().createQuery(criteriaQuery.orderBy()).unwrap(Query.class).getQueryString();
        Field f = CriteriaQueryTypeQueryAdapter.class.getDeclaredField("jpqlQuery");
        f.setAccessible(true);
        QueryImpl jpqlQuery = (QueryImpl) f.get(query);
        Map<String, TypedValue> stringTypedValueMap = jpqlQuery.getQueryParameters().getNamedParameters();
        String sql = getSql(jql);
        sql = String.format("select count(*) from (%1$s) t", sql);
        NativeQuery nativeQuery = (NativeQuery) getEm().createNativeQuery(sql);
        int index = 1;
        for (Iterator<Map.Entry<String, TypedValue>> iterator = stringTypedValueMap.entrySet().iterator(); iterator.hasNext(); ) {
            Map.Entry<String, TypedValue> next = iterator.next();
            nativeQuery.setParameter(index, next.getValue().getValue());
            index++;
        }
        return (Integer) nativeQuery.getSingleResult();
    }
default Long countByQueryEvent(QueryEvent event) {
    EntityManager entityManager = event.getEntityManager();
    Query query = event.getQuery();

    org.hibernate.Query hqlQuery = query.unwrap(HibernateQuery.class).getHibernateQuery();
    ASTQueryTranslatorFactory astQueryTranslatorFactory = new ASTQueryTranslatorFactory();
    HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) entityManager.getEntityManagerFactory();
    SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) hibernateEntityManagerFactory.getSessionFactory();
    QueryTranslator queryTranslator = astQueryTranslatorFactory.createQueryTranslator("", hqlQuery.getQueryString(), EMPTY_MAP, sessionFactory, null);
    queryTranslator.compile(EMPTY_MAP, false);
    String queryString = "select count(*) from (" + queryTranslator.getSQLString() + ") t";
    org.hibernate.Query countQuery = entityManager.createNativeQuery(queryString).unwrap(org.hibernate.Query.class);
    ParameterTranslations parameterTranslations = queryTranslator.getParameterTranslations();

    //is there any way to get all parameters with values? 
    // all parameters!!! not only provided by setParameter() method in Query
    Map params; 
    try {
        Field f = AbstractQueryImpl.class.getDeclaredField("namedParameters");
        f.setAccessible(true);
        params = (Map) f.get(hqlQuery);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
        return null;
    } catch (IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }

    for (Object param : parameterTranslations.getNamedParameterNames()) {
        String paramString = (String) param;
        final int[] positions =
                parameterTranslations.getNamedParameterSqlLocations(paramString);
        for (final int p : positions) {
            countQuery.setParameter(p, ((TypedValue)params.get(paramString)).getValue(),
                    ((TypedValue)params.get(paramString)).getType());
        }
    }

    Object result = countQuery.uniqueResult();
    if (result instanceof BigDecimal) {
        return ((BigDecimal) result).longValue();
    } else if (result instanceof BigInteger) {
        return ((BigInteger) result).longValue();
    } else {
        throw new IllegalArgumentException("JDBC driver returned unsupported resultType from count.");
    }
}

暂无
暂无

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

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