Spring Data JPA Projections: A Beginners Guide

Did you know that you can use Spring Data JPA projections to reduce the amount of data you fetch from the database and improve the performance of your queries? In this article, you will learn how to use projections effectively.

Eidan Khan
Make Android
6 min readJan 17, 2024

--

Spring Data JPA is a popular Java specification that simplifies the development of web applications that use data from relational databases. It offers many features and benefits, such as auto-configuration, query methods, repositories, and more. However, one of the challenges of using Spring Data JPA is how to optimize the queries and avoid fetching unnecessary data from the database. According to the Spring Data JPA documentation, projections can reduce the amount of data transferred between the database and the application by up to 80%, depending on the use case.” This can affect the performance, memory usage, and bandwidth of your application.

One way to solve this problem is to use Spring Data JPA projections. Projections are a feature that allows you to define interfaces that represent a subset of the properties of your domain entities. This way, you can customize the results of your queries and only retrieve the data you need. You can also use projections to transform the data into different shapes or formats, such as DTOs, JSON, XML, etc.

In this article, you will learn how to use projections effectively. By the end of this article, you will be able to use projections confidently and efficiently in your Spring Data JPA projects.

Prerequisites

Before you start writing the code for this article, you need to have the following tools, libraries, and frameworks installed on your machine:

  • Java 11 or higher
  • Spring Boot 2.5.4 or higher
  • Spring Data JPA 2.5.4 or higher
  • H2 Database 1.4.200 or higher
  • Maven 3.8.1 or higher:
  • IDE of your choice:

You can also download the complete source code for this article from here or use the following command to clone the repository:

git clone https://github.com/eidankhan/spring-data-jpa-projections-example.git

Setting Up the Project

To create a Spring Boot project with Spring Data JPA and H2 dependencies, you can use Spring Initializer, a web tool that generates a project skeleton for you. You can access Spring Initializer from here.

On the Spring Initializer page, you can choose the following options:

  • Project: Maven Project
  • Language: Java
  • Spring Boot: 2.5.4 or higher
  • Project Metadata: You can fill in the group, artifact, name, description, and package name as you like. For example, you can use com.projections as the group, spring-data-jpa-projections-example as the artifact, and com.projections.springdatajpaprojectionsexample as the package name.
  • Dependencies: You need to add the following dependencies to your project: Spring Web, Spring Data JPA, and H2 Database. You can search for them in the Add dependencies field and click on them to add them to your project.

After choosing the options, you can click on the Generate button to download a zip file containing your project. You can unzip the file and open it in your IDE of choice.

To configure the application properties, you need to edit the application.properties file in the src/main/resources folder. You can add the following properties to the file:

# Specify the database URL, username, and password
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=

# Enable the H2 console for accessing the database from the browser
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# Specify the JPA properties, such as the dialect, the show-sql flag, and the ddl-auto mode
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop

These properties will set up an in-memory H2 database for your application, enable the H2 console for accessing the database from the browser, and configure the JPA properties for your application.

Creating the Entity Classes

To use Spring Data JPA projections, you need to have some entity classes that represent your domain model. In this article, we will use a simple example of a Person and an Address entity. A Person has an id, a name, an email, and an address. An Address has an id, a street, a city, a state, and a zip code. A Person and an Address have a bidirectional one-to-one relationship, meaning that a Person has one and only one Address, and an Address belongs to one and only one Person.

To create the entity classes, you need to use the @Entity annotation to mark them as JPA entities. You also need to use the @Id annotation to mark the primary key field, and the @GeneratedValue annotation to specify the generation strategy for the primary key. You can use the @Column annotation to customize the column name, length, or nullable properties. You can use the @OneToOne annotation to define the one-to-one relationship between the entities, and the @JoinColumn annotation to specify the foreign key column. You can also use the @MapsId annotation to share the primary key value between the entities.

Here is the code for the Person entity class:

@Entity
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "name", length = 50, nullable = false)
private String name;

@Column(name = "email", length = 50, nullable = false, unique = true)
private String email;

@OneToOne
@JoinColumn(name = "address_id")
private Address address;

// Constructors, getters, setters, equals, hashcode, and toString methods
}

Here is the code for the Address entity class:

@Entity
public class Address {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "street", length = 50, nullable = false)
private String street;

@Column(name = "city", length = 50, nullable = false)
private String city;

@Column(name = "state", length = 50, nullable = false)
private String state;

@Column(name = "zip_code", length = 10, nullable = false)
private String zipCode;

@OneToOne(mappedBy = "address")
private Employee employee;

// Constructors, getters, setters, equals, hashcode, and toString methods
}

Creating the Projection Interfaces

A projection interface is an interface that represents a partial view of an entity class. It only contains the properties that you want to select from the entity class. You can use a projection interface to customize the results of your queries and improve the performance of your application.

To create a projection interface, you need to follow these steps:

  • Declare an interface with the name of your choice. For example, you can name it EmployeeDetails.
  • Add getter methods for the properties that you want to select from the entity class. The names of the methods must match the names of the properties in the entity class. For example, if you want to select the name, email, city and state of the employee you can add the following methods:
package com.projections.springdatajpaprojectionsexample.projection;

import org.springframework.beans.factory.annotation.Value;

public interface EmployeeDetails {
String getName();
String getEmail();
@Value("#{target.address.city}")
String getCity();
@Value("#{target.address.state}")
String getState();
}

Here, we use the @Value annotation to access the city property of the address object.

To use the projection interfaces in our repository methods, we just need to use them as the return type, as shown in the following example:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
EmployeeDetails findByEmail(String email);

}

Creating the Service Classes

A service class is a class that contains the business logic of your application. It usually interacts with the repository layer to perform CRUD operations on the entities. To create a service class for our example, you need to follow these steps:

  • Declare a class with the name of your choice. For example, you can name it EmployeeService.
  • Annotate the class with @Service to indicate that it is a Spring component that provides business services.
  • Inject the EmployeeRepository bean using the @Autowired annotation.
  • Define methods that use the projection interfaces as parameters or return types. For example, you can define a method that returns the employee details by email:
@Service
public class EmployeeService {

@Autowired
private EmployeeRepository employeeRepository;

public EmployeeDetails getEmployeeDetailsByEmail(String email) {
return employeeRepository.findByEmail(email);
}
}

Creating the Controller Classes

A controller class is a class that handles the HTTP requests from the clients. It usually interacts with the service layer to perform the required operations and return the appropriate responses. To create a controller class for our example, you need to follow these steps:

  • Declare a class with the name of your choice. For example, you can name it EmployeeController.
  • Annotate the class with @RestController to indicate that it is a Spring component that handles RESTful web services.
  • Inject the EmployeeService bean using the @Autowired annotation.
  • Define methods that map to the HTTP endpoints using the @GetMapping, @PostMapping, @PutMapping, or @DeleteMapping annotations. For example, you can define a method that returns the employee details by email using the @GetMapping annotation:
@RestController
public class EmployeeController {

@Autowired
private EmployeeService employeeService;

@GetMapping("/employees/{email}")
public EmployeeDetails getEmployeeDetailsByEmail(@PathVariable String email) {
return employeeService.getEmployeeDetailsByEmail(email);
}
}

In this article, we have explored the different basics of projections that Spring Data JPA offers to customize the results of our queries. We have learned how to use interface-based projections. Projections are a useful feature that can help us improve the performance and readability of our data access layers. They can also help us design our data transfer objects according to our needs and preferences. We hope you have enjoyed this article and learned something new. If you have any questions or feedback, please leave a comment below. Thank you for reading! 😊

--

--

Eidan Khan
Make Android

🚀 Full-stack Dev | Tech Content Creator 📝 For more in-depth articles, tutorials, and insights, visit my blog at JavaJams.org.