Mit den 'Fall zu sein...wenn...dann...sonst...Ende' Konstrukt im "having" - Klausel in der JPA criteria-query

Den folgenden Kriterien Abfrage berechnet den Durchschnitt der Bewertung von verschiedenen Gruppen von Produkten.

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();

Generiert es die folgende SQL-Abfrage :

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 

Den case...when Struktur ersetzt null Werte mit 0, wenn der angegebene Ausdruck in der case - Klausel ausgewertet, um null.

Brauche ich die gleiche case...when Konstrukt in der having - Klausel, so dass die Gruppe von Zeilen zurück, die durch die group by - Klausel gefiltert werden können, durch den Austausch null mit 0 in der Liste der berechneten Werte durch die case...when konstruieren, wenn überhaupt.

Entsprechend, die having Klausel erzeugt werden sollen, wie

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

Könnte es möglich sein, wenn in der greaterThanOrEqualTo() Methode selectExpression statt roundExpression ist gegeben, es ist aber nicht möglich. Dabei erzeugt eine compile-time Fehlermeldung "type mismatch" zwischen Expression<Integer> und Expression<Object>.

Also, wie kann ich die gleichen case...when Struktur in der having - Klausel wie in der select - Klausel?

ich habe auch versucht, durch das entfernen der generische Typ-parameter Object des Ausdrucks wie Expression selectExpression aber, so zu tun, verursacht die NullPointerException geworfen werden.


Außerdem, alias-Namen (prodId, rating) wie im select Klausel haben keine Auswirkung in der generierten SQL wie gesehen werden kann. Warum Spalten sind nicht alias hier? Bin ich etwas fehlt?

Wenn Spalten alias dann sollte es möglich sein zu schreiben, die having - Klausel wie folgt.

having rating>=0

sowie having in den Kriterien der Abfrage sollte wie folgt sein,

criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(join.<Integer>get("rating"), 0));

aber als Spalten nicht alias im select Klausel, wirft er eine exception.

java.lang.IllegalArgumentException: Unable to resolve attribute [rating] against path [null]

Was ist der Weg zu bekommen um mit dieser situation? Jedenfalls die Zeilen zurückgegeben, die von Group by gefiltert werden soll durch den Austausch null mit 0 in der Liste der erzeugten Werte durch case...when im select - Klausel.


Ich bin mit JPA 2.0 zur Verfügung gestellt von Hibernate 4.2.7 final.


EDIT:

Habe ich versucht mit dem folgenden Ausdruck :

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

aber es verursacht die folgende Ausnahme ausgelöst :

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)

Wie kann der folgende Ausdruck arbeiten dann,

Expression<Integer> roundExpression = criteriaBuilder
                              .function("round", Integer.class, quotExpression);

haben beide den gleichen Typ?

Gibt es einen Weg, um die case...when Struktur in der having - Klausel?


BEARBEITEN

Ändern der Typ des Ausdrucks zu

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

in EclipseLink (2.3.2) funktioniert folglich, es sein kann zur Verfügung gestellt in der having - Klausel.

Im Falle von Hibernate Anbieter, wirft Sie das NullPoiterExcpetion, wenn ein Versuch gemacht wird, um den Ausdruck zu ändern Art der selectCase() (die gibt Expression<Object> standardmäßig).


Update :

Dieses Problem weiterhin besteht, im Ruhezustand 5.0.5 Finale.

InformationsquelleAutor Tiny | 2013-11-20

Schreibe einen Kommentar