以下条件查询计算不同产品组的评分平均值。
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createQuery(Tuple.class);
Metamodel metamodel=entityManager.getMetamodel();
EntityType<Product>entityType=metamodel.entity(Product.class);
Root<Product>root=criteriaQuery.from(entityType);
SetJoin<Product, Rating> join = root.join(Product_.ratingSet, JoinType.LEFT);
Expression<Number> quotExpression = criteriaBuilder.quot(criteriaBuilder.sum(join.get(Rating_.ratingNum)), criteriaBuilder.count(join.get(Rating_.ratingNum)));
Expression<Integer> roundExpression = criteriaBuilder.function("round", Integer.class, quotExpression);
Expression<Object> selectExpression = criteriaBuilder.selectCase().when(quotExpression.isNull(), 0).otherwise(roundExpression );
criteriaQuery.select(criteriaBuilder.tuple(root.get(Product_.prodId).alias("prodId"), selectExpression.alias("rating")));
criteriaQuery.groupBy(root.get(Product_.prodId));
criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(roundExpression, 0));
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(Product_.prodId)));
TypedQuery<Tuple> typedQuery = entityManager.createQuery(criteriaQuery);
List<Tuple> tuples = typedQuery.getResultList();
它生成以下 SQL 查询:
SELECT product0_.prod_id AS col_0_0_,
CASE
WHEN Sum(ratingset1_.rating_num) / Count(ratingset1_.rating_num) IS
NULL THEN
0
ELSE Round(Sum(ratingset1_.rating_num) / Count(ratingset1_.rating_num))
END AS col_1_0_
FROM social_networking.product product0_
LEFT OUTER JOIN social_networking.rating ratingset1_
ON product0_.prod_id = ratingset1_.prod_id
GROUP BY product0_.prod_id
HAVING Round(Sum(ratingset1_.rating_num) / Count(ratingset1_.rating_num)) >= 0
ORDER BY product0_.prod_id DESC
The case...when
结构取代null
价值观与0
,如果指定的表达式case
子句被评估为null
.
我需要同样的case...when
构造在having
子句,以便返回的行组group by
可以通过替换来过滤子句null
with 0
在计算值列表中case...when
构造,如果有的话。
据此,having
子句应该像这样生成
HAVING
(CASE
WHEN Sum(ratingset1_.rating_num)/Count(ratingset1_.rating_num) IS
NULL THEN 0
ELSE Round(sum(ratingset1_.rating_num)/Count(ratingset1_.rating_num))
END)>=0
这是可能的,如果在greaterThanOrEqualTo()
方法,selectExpression
代替roundExpression
已给出,但这是不可能的。这样做会生成一个编译时错误,指示之间的类型不匹配Expression<Integer>
and Expression<Object>
.
那么我怎样才能拥有相同的case...when
结构中的having
条款如select
clause?
I have also tried by removing the generic type parameter Object
of the expression like Expression selectExpression
but doing so, caused the NullPointerException
to be thrown.
此外,别名(prodId
, rating
)如给出的select
可以看出,子句对生成的 SQL 没有影响。为什么这里的列没有别名?我错过了什么吗?
如果列有别名,那么应该可以写having
子句如下。
having rating>=0
and having
在条件查询中应如下所示,
criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(join.<Integer>get("rating"), 0));
但由于列在中没有别名select
子句,它会抛出异常。
java.lang.IllegalArgumentException: Unable to resolve attribute [rating] against path [null]
有什么办法可以解决这种情况呢?无论如何,返回的行Group by
应通过替换来过滤null
with 0
在产生的值列表中case...when
in the select
clause.
我正在使用 Hibernate 4.2.7 Final 提供的 JPA 2.0。
EDIT:
我尝试过使用以下表达式:
Expression<Integer> selectExpression = criteriaBuilder.<Integer>selectCase()
.when(quotExpression.isNull(), 0)
.<Integer>otherwise(roundExpression);
但它导致抛出以下异常:
Caused by: java.lang.NullPointerException
at java.lang.Class.isAssignableFrom(Native Method)
at org.hibernate.ejb.criteria.ValueHandlerFactory.isNumeric(ValueHandlerFactory.java:69)
at org.hibernate.ejb.criteria.predicate.ComparisonPredicate.<init>(ComparisonPredicate.java:69)
at org.hibernate.ejb.criteria.CriteriaBuilderImpl.greaterThanOrEqualTo(CriteriaBuilderImpl.java:468)
那么下面的表达式如何工作,
Expression<Integer> roundExpression = criteriaBuilder
.function("round", Integer.class, quotExpression);
两者有相同的类型吗?
有没有办法把case...when
结构中的having
clause?
EDIT
将表达式类型更改为
Expression<Integer> selectExpression = criteriaBuilder
.<Integer>selectCase()
.when(quotExpression.isNull(), 0)
.<Integer>otherwise(roundExpression);
in EclipseLink (2.3.2)因此,它可以在having
条款。
如果是 Hibernate 提供程序,它会抛出NullPoiterExcpetion
,如果尝试更改表达式类型selectCase()
(返回Expression<Object>
默认情况下)。
Update :
这个问题在 Hibernate 5.0.5 Final 中仍然存在。