Spring Data Aggregate Functions in a repository

ALT TEXT

In this tutorial, we are taking a look at a student's question from my Spring Boot Introduction Course.

Hi Dan, How can I use the @Query to specify an aggregation function in a select statement like... "select post_date, sum(value) from post group by post_date" retrieve it to a DTO and show on screen?

This is actually pretty easy to do using the @Query annotation. In this tutorial, I will show you how to do this using JQL and native SQL. 

Creating the application

The first thing we need to do is to create a very basic Spring Boot application with the following dependencies. If you want you can grab the source code for this demo here

Aggregate Functions Dependencies

I won't walk you through every single step of this application because I don't want to focus on the application itself. We are building a simple application that exposes some endpoints that call a service and a repository.  

Aggregate Functions Project Structure

This is what our domain looks like. 

@Entity
@Data
@NoArgsConstructor
public class User {

    @Id @GeneratedValue
    private long id;
    private String firstName;
    private int age;

    public User(String first, int age) {
        this.firstName = first;
        this.age = age;
    }

}

And we will load some initial test data using a Command Line Runner

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    CommandLineRunner runner(UserRepository userRepository){
        return args -> {
            userRepository.save( new User("Sam", 20) );
            userRepository.save( new User("Joe", 25) );
            userRepository.save( new User("Mark", 48) );
            userRepository.save( new User("Emily", 26) );
            userRepository.save( new User("Nick", 59) );
        };
    }

}

With those in place, we can now begin to add some new methods to our repositories. 

Spring Data Repositories

In the first example, I want to find out the average age of all the users in our system. We can do so by using the Aggregate function AVG in both JPQL and SQL. We can write these queries using the @Query annotation

public interface UserRepository extends CrudRepository<User, Long> {

    @Query("SELECT AVG(u.age) from User u")
    int getAverageAge();

}

In this example, we are using JPQL to write the SQL that will fetch us the data we need. JPQL stands for Java Persistence Query Language and if you have ever worked with Hibernate before you have probably seen this in action. It is important to understand that this is the default and if you want to write native SQL you can, but you need to add a flag, more on that in a bit.  Now let's say that we wanted to add a new method that would give us the max age of all the users but also allow us to exclude a single user. This is strictly a demo and you probably wouldn't use this in a real application but it does show off a couple of things. First, it shows us how to include parameters in our SQL statement and it also shows us that we can write native SQL by using the native SQL flag. 

package com.therealdanvega.repository;

import com.therealdanvega.domain.User;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {

    @Query("SELECT AVG(u.age) from User u")
    int getAverageAge();

    @Query(value = "SELECT max(age) from User where first_name <> ?1", nativeQuery = true)
    int getMaxAgeMinus(String name);

}

Screencast 

https://www.youtube.com/watch?v=zLRavueFJy0

Conclusion

I think the one that confuses people is not understanding that the default query syntax for @Query annotation is JPQL and that it does support aggregate functions. If you are interested in the full source code for this demo you can grab it here

_Question: Are you facing any issues with your Spring Data Repositories? _

Subscribe to my newsletter.

Sign up for my weekly newsletter and stay up to date with current blog posts.

Weekly Updates
I will send you an update each week to keep you filled in on what I have been up to.
No spam
You will not receive spam from me and I will not share your email address with anyone.