18

I was trying to return an average and count of a set of ratings in one query. I managed it fairly easily in two queries following the example I found browsing. For example:

@Query("SELECT AVG(rating) from UserVideoRating where videoId=:videoId")
public double findAverageByVideoId(@Param("videoId") long videoId);

but as soon as I wanted an average and a count in the same query, the trouble started. After many hours experimenting, I found this worked, so I am sharing it here. I hope it helps.

1) I needed a new class for the results:

The I had to reference that class in the query:

@Query("SELECT new org.magnum.mobilecloud.video.model.AggregateResults(AVG(rating) as rating, COUNT(rating) as TotalRatings) from UserVideoRating where videoId=:videoId")
public AggregateResults findAvgRatingByVideoId(@Param("videoId") long videoId);

One query now returns average rating and count of ratings

1
  • does this populate other attribites as well. Commented Dec 2, 2021 at 21:20

2 Answers 2

23

Solved myself.

Custom class to receive results:

public class AggregateResults {

    private final double rating;
    private final int totalRatings;

    public AggregateResults(double rating, long totalRatings) {
        this.rating = rating;
        this.totalRatings = (int) totalRatings;
    }

    public double getRating() {
        return rating;
    }

    public int getTotalRatings() {
        return totalRatings;
    }
}

and

@Query("SELECT new org.magnum.mobilecloud.video.model.AggregateResults(
    AVG(rating) as rating, 
    COUNT(rating) as TotalRatings) 
    FROM UserVideoRating
    WHERE videoId=:videoId")
public AggregateResults findAvgRatingByVideoId(@Param("videoId") long videoId);
Sign up to request clarification or add additional context in comments.

6 Comments

Please define your custom class
public class AggregateResults { private final double rating; private final int totalRatings; public AggregateResults(double rating, long totalRatings) { this.rating = rating; this.totalRatings = (int) totalRatings; } public double getRating() { return rating; } public int getTotalRatings() { return totalRatings; } }
thank you...Please add this to your answer and relate
Is your custom class marked as "Entity"? I've got error "entity has no primary key attribute defined"
@jmhostalet You need (at)Entity against the classes that map to tables, yes, but not the custom result class. Your error sounds like an issue with the class mapped to the db table. You need to have a column tagged with (at)Id somewhere in that class. For example: (at)Id (at)GeneratedValue(strategy = GenerationType.AUTO) private long id; Sorry the dialogue won't let me enter "at" signs
|
4

Thanks.

You should prevent NPEs and hibernate parsing tuple errors as following :

public class AggregateResults {

private final double rating;
private final int totalRatings;

public AggregateResults(Double rating, Long totalRatings) {
    this.rating = rating == null ? 0 : rating;
    this.totalRatings = totalRatings == null ? 0 : totalRatings.intValue();
}

public double getRating() {
    return rating;
}
public int getTotalRatings() {
    return totalRatings;
}}

1 Comment

Yes, I guess the AVG() could return null although I think the COUNT() would be 0, if no rows matched.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.