在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造

在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造,第1张

在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造

这不太可能是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中一个长期存在的错误,尚待修复-



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5102525.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-17
下一篇 2022-11-17

发表评论

登录后才能评论

评论列表(0条)

保存