尝试使用现有条件复制计数的 Critria 构建器时。在 Hibernate 6 中似乎出现以下错误,但在 Hibernate 5 中似乎同样有效。
原因:java.lang.IllegalArgumentException:已经注册了一个副本:
SqmBasicValuedSimplePath(com.example.domain.Test(175781908930100).name)
添加完整代码。
public static <Q, R> Page<Q> getResultsPage(final EntityManager entityManager, final CriteriaQuery<Q> criteria, final Root<R> root, final Pageable pageable,
final List<Order> defaultOrderList) {
return PageableExecutionUtils.getPage(getResultList(entityManager, criteria, root, pageable, defaultOrderList), pageable, () -> count(entityManager, criteria));
}
public static <Q, R> List<Q> getResultList(final EntityManager entityManager, final CriteriaQuery<Q> criteria, final Root<R> root, final Pageable pageable,
final List<Order> defaultOrderList) {
CriteriaUtils.setOrderBy(entityManager, criteria, root, pageable, defaultOrderList);
TypedQuery<Q> resultQuery = entityManager.createQuery(criteria);
if (Objects.nonNull(pageable) && pageable.isPaged()) {
resultQuery.setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize());
}
return resultQuery.getResultList();
}
public static <T> Long count(EntityManager em, CriteriaQuery<T> criteria) {
return em.createQuery(countCriteria(em, criteria)).getSingleResult();
}
public static <T> CriteriaQuery<Long> countCriteria(EntityManager em, CriteriaQuery<T> criteria) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);
copyCriteriaWithoutSelectionAndOrder(criteria, countCriteria);
Expression<Long> countExpression;
if (criteria.isDistinct()) {
countExpression = builder.countDistinct(findRoot(countCriteria, criteria.getResultType()));
} else {
countExpression = builder.count(findRoot(countCriteria, criteria.getResultType()));
}
return countCriteria.select(countExpression);
}
private static void copyCriteriaWithoutSelectionAndOrder(
CriteriaQuery<?> from, CriteriaQuery<?> to) {
// Copy Roots
for (Root<?> root : from.getRoots()) {
Root<?> dest = to.from(root.getJavaType());
dest.alias(getOrCreateAlias(root));
copyJoins(root, dest);
}
to.groupBy(from.getGroupList());
to.distinct(from.isDistinct());
if (from.getGroupRestriction() != null)
to.having(from.getGroupRestriction());
Predicate predicate = from.getRestriction();
if (predicate != null)
to.where(predicate);
}
似乎在 Hibernate 6 中不再可能以这种方式跨不同的
CriteriaQueries
重用对象。我们在 CriteriaQueries
中遇到了这个错误,我们将谓词从一个查询复制到另一个查询,解决方法是重构代码,以便为每个 Predicate
创建一个新的 CriteriaQuery
对象(请参阅我对类似问题的回答) )。不幸的是,您似乎更难做到这一点 - 也许您可以使用此处讨论的 SQM 复制功能?
这个问题是在https://hibernate.atlassian.net/browse/HHH-15951
中提出的您需要将 hibernate-core 更新到版本 6.4.4.Final
然后在您的
countCriteria
中,更新这两行
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);
进入
HibernateCriteriaBuilder hibernateCriteriaBuilder = (HibernateCriteriaBuilder) criteriaBuilder;
JpaCriteriaQuery<Long> countQuery = hibernateCriteriaBuilder.createQuery(Long.class);
那么
countCriteria
方法应该返回 JpaCriteriaQuery
而不是 CriteriaQuery
现在更新
public static <T> Long count(EntityManager em, CriteriaQuery<T> criteria) {
return em.createQuery(countCriteria(em, criteria).createCountQuery()).getSingleResult();
}
这应该可以解决问题。
有关更多信息,您可以查看此示例(在
testParameters
方法下):https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/test/java/org/ hibernate/orm/test/query/criteria/CountQueryTests.java
希望有帮助