Результаты подсчета запросов JPA Critera, включающие соединение

Я использую спящий режим и API критериев JPA и пытаюсь создать повторно используемый служебный метод, чтобы определить, сколько строк будет возвращено запросом.

В настоящее время у меня есть это:

Long countResults(CriteriaQuery cq, String alias){
    CriteriaBuilder cb = em().getCriteriaBuilder();
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root ent = countQuery.from(cq.getResultType());
    ent.alias(alias);
    countQuery.select(cb.count(ent));
    Predicate restriction = cq.getRestriction();
    if(restriction != null){
        countQuery.where(restriction);
    }
    return em().createQuery(countQuery).getSingleResult();
}

Который я использую так:

CriteriaBuilder cb = em().getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(modelClass());
root.alias("ct");
cq.select(root);

TypedQuery<User> query = em().createQuery(cq);
long count = countResults(cq, "ct");

И это прекрасно работает. Однако, когда я использую более сложный запрос, например

Join<UserThing, Thing> j = root.join(User_.things).join(UserThing_.thing);
cq.where(somePredicate);

Мой вызов countResults() приводит к таким исключениям, как org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'myAlias.name', <AST>:0:0: unexpected end of subtree, left-hand operand of a binary operator was null

Я предполагаю, что это как-то связано с объединением, и что мне нужно как-то связать это, но пока я не добился никакого успеха.

Помощь?


person kag0    schedule 02.06.2016    source источник
comment
Вы смотрели на добавление «отличных» к вашему счету? Соединения между коллекциями создают дубликаты значений строк, которые вы подсчитываете, которые затем будут отфильтрованы Distinct. Вы можете вызвать .distinct(true) для того, что вы передаете для подсчета   -  person Chris    schedule 02.06.2016
comment
@Chris попробовал, но безрезультатно.   -  person kag0    schedule 02.06.2016
comment
Вы подсчитываете все строки, возвращенные из вашего внутреннего запроса. вместо того, чтобы возвращать пользователя, возвращайте только отдельные идентификаторы пользователей. Посмотрите на сгенерированный SQL и посмотрите, как он повлияет на ваши данные.   -  person Chris    schedule 03.06.2016
comment
Вы уже решили эту проблему? У меня такая же проблема!   -  person meobeo173    schedule 10.11.2016
comment
@meobeo173 переключился на jooq, ха-ха. Гектор только что добавил ответ, попробуйте и дайте нам всем знать, если это сработает.   -  person kag0    schedule 10.02.2017
comment
Да, я решил с помощью QueryDSL, в любом случае переход на JOOQ - отличная идея, наш продукт перегружен JPA.   -  person meobeo173    schedule 14.02.2017


Ответы (1)


У меня была такая же проблема, и я решил с помощью:

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.

person Hector    schedule 10.02.2017
comment
Это не сработает, если существует более одного уровня соединений (т. е. соединения на соединениях). - person Amalgovinus; 22.02.2017