程序问答   发布时间:2022-06-02  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

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

开发过程中遇到在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造的问题如何解决?下面主要结合日常开发的经验,给出你关于在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造的解决方法建议,希望对你解决在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_raTing
FROM
    product p
left OUTER JOIN
    raTing r
        ON p.prod_ID=r.prod_ID
GROUP 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中。

@H_254_7@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与另一个表明显多到一的关系productprod_ID是外键引用的主键prod_IDproduct表)。

在这个问题中,我们只对子句中的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()上面的第二个参数。是的0criteriaBuilder.literal(0)因此,应该是问题中提到的例外。

因此,CriteriaBuilder#literal(T value)CriteriaBuilder#SELEctCase()构造中使用表达式时,请始终坚持在上述必要时始终使用文字值。

在Hibernate 4.3.6最终版,Hibernate 5.0.5最终版上进行测试。稍后,我将尝试在Eclipselink(最终版2.6.1)上运行相同的查询。 不再有古怪之处。

Eclipselink的查询的修改版本完全没有问题,只不Object如果使用构造函数表达式代替Tuple该问题,则它需要在构造函数参数(形式参数)中使用类型参数。这是Eclipselink中一个长期存在的错误,尚待修复-

解决方法

以下条件查询计算不同产品组的平均评分。

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

case...when结构取代null值与0,如果在指定的表达式case子句进行评估以null

我需要case...whenhaving子句中使用相同的构造,以便group by可以通过用该构造所计算的值列表中的替换null为来过滤由子句返回的行组。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>和之间的类型不匹配Expression<Object>

那么case...when,该having子句中如何与该子句具有相同的结构SELEct

我也尝试过删除Object表达式的泛型类型参数,Expression SELEctExpression但这样做NullPointerException会引发。


此外,别名(prodIdraTing作为中给出)SELEct子句具有在生成的SQL没有影响,因为可以看到。为什么列在这里没有别名?我想念什么吗?

如果列是别名,那么应该可以having像下面这样写子句。

having raTing>=0

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为来过滤由返回的行。0``case...when``SELEct


我正在使用Hibernate 4.2.7 final提供的JPA 2.0


编辑:

我尝试使用以下表达式:

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",quotExpression);

两者具有相同的类型?

有没有办法将case...when结构放在having子句中?


编辑

将表达式类型更改为

Expression<Integer> SELEctExpression = criteriaBuilder
                                       .<Integer>SELEctCase()
                                       .when(quotExpression.isNull(),0)
                                       .<Integer>otherwise(roundExpression);

因此,在 EclipseLink(2.3.2)中 工作时,可以在该having子句中使用它。

对于Hibernate提供程序,NullPoiterExcpetion如果尝试更改的表达式类型SELEctCase()Expression<Object>默认返回),则抛出。


更新:

这个问题在Hibernate 5.0.5 final中仍然存在。

大佬总结

以上是大佬教程为你收集整理的在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造全部内容,希望文章能够帮你解决在JPA条件查询的“具有”子句中使用“ case ... when ... then ... else ... end”构造所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。