Logo

dev-resources.site

for different kinds of informations.

How To Fetch Data By Using DTO Projection In Spring Data JPA

Published at
11/30/2024
Categories
java
jpa
spring
hibernate
Author
davidnguyen15
Categories
4 categories in total
java
open
jpa
open
spring
open
hibernate
open
Author
13 person written this
davidnguyen15
open
How To Fetch Data By Using DTO Projection In Spring Data JPA

Introduction

In this post, we'll explore how projections work in Spring Data JPA, discuss different types, and walk through examples to demonstrate how they can simplify data access.

For this guide, we’re using:

  • IDE: IntelliJ IDEA (recommended for Spring applications) or Eclipse
  • Java Version: 17
  • Spring Data JPA Version: 2.7.x or higher (compatible with Spring Boot 3.x)
  • Entities Used: User (representing a user profile) and Address (representing a user’s address details)

NOTE: For more detailed examples, please visit my GitHub repository here

@Setter
@Getter
@Entity(name = "tbl_address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String street;
    private String city;
    private String state;
    private String country;
    private String zipCode;
}
Enter fullscreen mode Exit fullscreen mode
@Setter
@Getter
@Entity(name = "tbl_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstName;
    private String lastName;
    private String email;
    private String status;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;
}
Enter fullscreen mode Exit fullscreen mode

1. Why use Projections in Spring Data JPA?

Often, your application only requires a subset of an entity's fields and loading unnecessary data can lead to:

  • Increased memory usage
  • Slow queries
  • Complex entity management when working with joined data

Projections come to help us avoid issues by enabling you to fetch only the data you need and in the exact format you need. This is especially useful when fetching data for RESTful APIs where not all fields of an entity are required for the response.

2. Type of Projections in Spring Data JPA.

Spring Data JPA offers several types of projections:

  • Interface-based Projections
  • Class-based Projections (DTO projection)

2.1 - Interface-based Projections

Interface-based projections allow us to define an interface with getter methods for the fields you want to retrieve. Spring Data JPA will then use these getters to map the entity's fields to the interface.

  • Example:

Define a projection interface and repository class:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("""
        SELECT
            concat(u.firstName, ' ', u.lastName) as fullName,
            u.email as email,
            concat( a.street, ', ', a.city, ', ', a.state) as fullAddress,
            a.country as country,
            a.zipCode as zipCode
        FROM tbl_user u
        LEFT JOIN tbl_address a ON  u.address.id = a.id
    """)
    List<UserInfoProjection> findAllUserInfo();

    interface UserInfoProjection {
        String getFullName();
        String getEmail();
        String getFullAddress();
        String getCountry();
        String getZipCode();
    }
}
Enter fullscreen mode Exit fullscreen mode

Define a DTO class to transfer from projection to dto.

@Builder
@Setter
@Getter
public class UserDTO {
    private String fullName;
    private String email;
    private String address;
    private String country;
    private String zipCode;

    public static UserDTO of(UserRepository.UserInfoProjection entity) {
        if (Objects.isNull(entity))
            return null;

        return UserDTO.builder()
                .fullName(entity.getFullName())
                .email(entity.getEmail())
                .address(entity.getFullAddress())
                .country(entity.getCountry())
                .zipCode(entity.getZipCode())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Testing:
@SpringBootTest
@AutoConfigureMockMvc
class QueryTypesApplicationTests {
    @Autowired
    private UserRepository userRepository;

    @Test
    public void testDerivedQueryMethods() {
        List<UserDTO> results =  userRepository.findAllUserInfo()
                .stream()
                .map(UserDTO::of)
                .toList();

        assertEquals(10, results.size(), "Expected 10 users");
    }

}
Enter fullscreen mode Exit fullscreen mode

2.2 - Class-based Projections

With class-based projections, we can use a custom DTO to map the results directly. This approach gives you more control over the structure of your data and can be useful if you need custom logic in the constructor.

  • Example:

Define DTO:

@Setter
@Getter
public class UserProjectionDTO {
    private final String fullName;
    private final String email;
    private final String address;
    private final String country;
    private final String zipCode;

    public UserProjectionDTO(String fullName, String email, String address, String country, String zipCode) {
        this.fullName = fullName;
        this.email = email;
        this.address = address;
        this.country = country;
        this.zipCode = zipCode;
    }
}
Enter fullscreen mode Exit fullscreen mode

Define repository class and write sql query for getting user information.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    @Query(
        """
            SELECT new com.davidnguyen.querytypes.user.UserProjectionDTO(
                concat(u.firstName, ' ', u.lastName),
                u.email,
                concat(a.street, ', ', a.city, ', ', a.state),
                a.country,
                a.zipCode
            )
            FROM tbl_user u LEFT JOIN tbl_address a ON u.address.id = a.id
        """
    )
    List<UserProjectionDTO> findAllUserInfo();
}
Enter fullscreen mode Exit fullscreen mode

Spring Data JPA executes a query that constructs a DTO for each row of data, selecting only the fields specified in the constructor.

  • Testing:
@SpringBootTest
@AutoConfigureMockMvc
class QueryTypesApplicationTests {
    @Autowired
    private UserRepository userRepository;

    @Test
    public void testDerivedQueryMethods() {
        List<UserProjectionDTO> users = userRepository.findAllUserInfo();

        assertEquals(10, users.size(), "Expected 10 users");
    }

}
Enter fullscreen mode Exit fullscreen mode

3. Choosing the right Projection type

Each projection type has its use case:

  • Interface-based projections are ideal for simple field selections.
  • Class-based projection are better for complex transformations or custom logic.

Performance notes:

  • Complex projections can lead too more complex queries, which may impact performance.
  • Class-based projections using DTOs my introduce overhead, especially for large result sets, because each row requires a new DTO instance. So always monitor and optimize queries as needed.

4. Best practices for DTO projections in Spring Data JPA.

  • Select Only the Fields You Need: Whether using class or interface-based projections, always limit the selection to only the necessary fields to optimize database load.

  • Use Immutability for DTOs: For class-based projections, create DTOs that are immutable (final fields, no setters) to make them safe and stable.

  • Consider Native Queries for Complex Joins: If your projections involve complex joins or calculated fields, consider using a native SQL query with @SqlResultSetMapping and @ConstructorResult annotations for more control.

  • Profile Queries for Performance: Especially with large datasets, use profiling tools (like JPA/Hibernate logging) to monitor the query performance and ensure that your projections aren’t inadvertently loading extra data.

Wrapping up

Projections in Spring Data JPA offer powerful options for controlling the data returned from the queries. By using interface-based, class-based and dynamic projections, you can fine-tune data retrieval to match you application's requirements. And keep in mind that, implementing projections effectively leads to more efficient data handling, especially in applications with large data sets.

See you in the next posts. Happy Coding!

Visit my blog for more posts.

hibernate Article's
30 articles in total
Favicon
JOOQ Is Not a Replacement for Hibernate. They Solve Different Problems
Favicon
Persistence Context в Hibernate Zoo: путешествие объекта по жизненным состояниям
Favicon
Unidirectional associations for one-to-many
Favicon
Como eu reduzi em até 99% o tempo de resposta da minha API
Favicon
Hibernate Zoo: Жадный Гиппопотам и Ленивый Лемур (Lazy vs Eager)
Favicon
🐾 Hibernate Zoo: Путеводитель по языкам запросов в мире данных 🐾
Favicon
How To Fetch Data By Using DTO Projection In Spring Data JPA
Favicon
Ubuntu 22.04 Hibernate Using Swap File
Favicon
Зоопарк Hibernate: N+1 запросов или как накормить жадного бегемота
Favicon
Spring Data JPA Stream Query Methods
Favicon
Uma breve introdução ao Hibernate
Favicon
Ubuntu hibernate
Favicon
Eager vs Lazy Initialization of Spring Beans
Favicon
Understanding JPA Mappings in Spring Boot: One-to-One, One-to-Many, Many-to-One, and Many-to-Many Relationships
Favicon
Java Hibernate vs JPA: Rapid review for you
Favicon
Hibernate Connection Library with GUI Generation
Favicon
what is JPA? explain few configurations
Favicon
Demystifying Hibernate: A Beginner's Journey
Favicon
How to deal with N+1 problems with Hibernate
Favicon
Java Hibernate vs JPA: Quick Review
Favicon
Uppercase table names in Spring Boot
Favicon
Hiring Alert - Java Developer- Blockchain
Favicon
H2 database Setup Error Unable to load name org.hibernate.dialect.Oracle10gDialect
Favicon
Capitalisation of table name generated by Hibernate when using MySQL server
Favicon
Display SQL statement generated by Hibernate JPA in Spring Boot environment
Favicon
Advanced and dynamic searching with Spring Data JPA
Favicon
Defining JPA/Hibernate Entities in Kotlin
Favicon
Criteria Queries and JPA Metamodel with Spring Boot and Kotlin
Favicon
How to save Hibernate Entity changes to Database
Favicon
Hibernate Cheat Sheet

Featured ones: