使用hibernate供应商计算JPA 2.0的条件查询

时间:2017-05-16 05:35:14

标签: java hibernate jpa

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

例如在plane 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

我希望创建这样的东西(我知道在这个特定的查询中,一个不同的计数将解决问题,但我需要一个通用的解决方案,我想在非jpa托管返回类型查询中使用它进行分页): / p>

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

中使用

然后我认为我可以将它转换为Subquery,因为CriteriaQuery和Subquery都实现了相同的接口,但是Subquery也实现了Expression。

但这不符合我的想法。这是我目前的非工作代码:

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中是否可行?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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.");
    }
}