Я хочу выполнить пару запросов, используя один и тот же массив Predicate
: один для подсчета записей, другой для получения определенной страницы записей. Мне кажется, это довольно нормальный вариант использования, поэтому должен быть хороший способ сделать это, но я еще не нашел его.
Так что это часть, которая выбирает сущности:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
EntityType<ENTITY> entityType = entityManager.getMetamodel().entity(FooEntity.class);
CriteriaQuery<FooEntity> entityQuery = criteriaBuilder.createQuery(FooEntity.class);
Root<FooEntity> entityRoot = entityQuery.from(FooEntity.class);
// Use the criteria builder, root, and type to create some predicates.
Predicate[] predicates = createPredicates(criteriaBuilder, entityRoot, entityType );
// Fetch the entities.
entityQuery.select(entityRoot);
entityQuery.where(predicates);
List<FooEntity> entities = entityManager.createQuery(entityQuery)
.setFirstResult(0) // Just get the first page
.setMaxResults(50)
.getResultList();
Это работает. Мы получаем то, что хотим, предикаты верны и т. Д. c.
Однако создать другой запрос для определения количества с использованием тех же предикатов не удается. Я пробовал два разных способа:
(1) Повторное использование Root
:
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(entityRoot));
countQuery.where(predicates);
long count = entityManager.createQuery(countQuery).getSingleResult();
Это не работает и дает мне java.lang.IllegalStateException: No criteria query roots were specified
. Странно, поскольку я четко указал Root
, но, возможно, я не могу повторно использовать Root
, который был создан из другого CriteriaQuery
? Хорошо, давайте создадим один из тех же CriteriaQuery
...
(2) Создание нового Root
:
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(countQuery.from(FooEntity.class));
countQuery.where(predicates);
long count = entityManager.createQuery(countQuery).getSingleResult();
Теперь мы получаем другую ошибку: org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.fooProperty' [select count(generatedAlias0) from com.foo.FooEntity as generatedAlias0 where ( generatedAlias1.fooProperty = :param0 )]
Глядя на созданный HQL, выясняется, что в предложении "from" задается generatedAlias0
, но все содержимое в предложении "where" ссылается на generatedAlias1
. Я предполагаю, что массив Predicate
был построен с использованием Root
, отличного от того, который использовался в CriteriaQuery<Long>
.
Так что, если он не работает в любом случае, как бы я использовал повторно? тот же массив Predicate
? Неужели мне нужно воссоздать их все со вторым Root
? Это кажется мне чрезмерным, тем более что они оба Root<FooEntity>
. Я чувствую, что должен быть лучший способ.