这不太可能是Hibernate中的错误。在制作给定的标准查询时存在技术错误。以相同的示例但形式更简单。
假设我们对生成以下SQL查询感兴趣。
SELECt p.prod_id, p.prod_name, CASE WHEN sum(r.rating_num)/count(DISTINCT r.rating_id) IS NULL THEN 0 ELSE round(sum(r.rating_num)/count(DISTINCT r.rating_id)) END AS avg_ratingFROM product pLEFT OUTER JOIN rating r ON p.prod_id=r.prod_idGROUP BY p.prod_id, p.prod_name HAVINg CASE WHEN sum(r.rating_num)/count(DISTINCT r.rating_id) IS NULL THEN 0 ELSE round(sum(r.rating_num)/count(DISTINCT r.rating_id)) END>=1
基于下表在MySQL中。
mysql> desc rating;+-------------+---------------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------------+---------------------+------+-----+---------+----------------+| rating_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment || prod_id | bigint(20) unsigned | YES | MUL | NULL | || rating_num | int(10) unsigned | YES | | NULL | || ip_address | varchar(45) | YES | | NULL | || row_version | bigint(20) unsigned | NO | | 0 | |+-------------+---------------------+------+-----+---------+----------------+5 rows in set (0.08 sec)
此表
rating与另一个表明显多到一的关系
product(
prod_id是外键引用的主键
prod_id的
product表)。
在这个问题中,我们只对子句中的
CASE构造感兴趣
HAVINg。
以下条件查询,
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();CriteriaQuery<Tuple> criteriaQuery = criteriaBuilder.createTupleQuery();Root<Product> root = criteriaQuery.from(entityManager.getmetamodel().entity(Product.class));ListJoin<Product, Rating> prodRatingJoin = root.join(Product_.ratingList, JoinType.LEFT);List<expression<?>> expressions = new ArrayList<expression<?>>();expressions.add(root.get(Product_.prodId));expressions.add(root.get(Product_.prodName));expression<Integer> sum = criteriaBuilder.sum(prodRatingJoin.get(Rating_.ratingNum));expression<Long> count = criteriaBuilder.countDistinct(prodRatingJoin.get(Rating_.ratingId));expression<Number> quotexpression = criteriaBuilder.quot(sum, count);expression<Integer> roundexpression = criteriaBuilder.function("round", Integer.class, quotexpression);expression<Integer> selectexpression = criteriaBuilder.<Integer>selectCase().when(quotexpression.isNull(), criteriaBuilder.literal(0)).otherwise(roundexpression);expressions.add(selectexpression);criteriaQuery.multiselect(expressions.toArray(new expression[0]));expressions.remove(expressions.size() - 1);criteriaQuery.groupBy(expressions.toArray(new expression[0]));criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(selectexpression, criteriaBuilder.literal(1)));List<Tuple> list = entityManager.createQuery(criteriaQuery).getResultList();for (Tuple tuple : list) { System.out.println(tuple.get(0) + " : " + tuple.get(1) + " : " + tuple.get(2));}
按预期生成以下正确的SQL查询。
select product0_.prod_id as col_0_0_, product0_.prod_name as col_1_0_, case when sum(ratinglist1_.rating_num)/count(distinct ratinglist1_.rating_id) is null then 0 else round(sum(ratinglist1_.rating_num)/count(distinct ratinglist1_.rating_id)) end as col_2_0_ from projectdb.product product0_ left outer join projectdb.rating ratinglist1_ on product0_.prod_id=ratinglist1_.prod_id group by product0_.prod_id , product0_.prod_name having case when sum(ratinglist1_.rating_num)/count(distinct ratinglist1_.rating_id) is null then 0 else round(sum(ratinglist1_.rating_num)/count(distinct ratinglist1_.rating_id)) end>=1
从技术角度来看,请查看上述条件查询中的以下行。
criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(selectexpression, criteriaBuilder.literal(1)));
问题中类似的代码如下。
createQuery.having(criteriaBuilder.greaterThanOrEqualTo(selectexpression, 1));
看到问题中的原始表达式做的完全相同:
expression<Integer> selectexpression = criteriaBuilder.<Integer>selectCase() .when(quotexpression.isNull(), 0) .<Integer>otherwise(roundexpression);
尝试将该表达式传递给
criteriaBuilder.greaterThanOrEqualTo()以下对象。
criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(selectexpression, 0));
请特别注意
greaterThanOrEqualTo()上面的第二个参数。是的
0。
criteriaBuilder.literal(0)因此,应该是问题中提到的例外。
因此,
CriteriaBuilder#literal(Tvalue)在
CriteriaBuilder#selectCase()构造中使用表达式时,请始终坚持在上述必要时始终使用文字值。
在Hibernate 4.3.6最终版,Hibernate 5.0.5最终版上进行测试。稍后,我将尝试在Eclipselink(最终版2.6.1)上运行相同的查询。 不再有古怪之处。
Eclipselink的查询的修改版本完全没有问题,只不过
Object如果使用构造函数表达式代替
Tuple该问题,则它需要在构造函数参数(形式参数)中使用类型参数。这是Eclipselink中一个长期存在的错误,尚待修复-
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)