Spring Data JPA Java Record DTO Projection

1. Introduction

With Java 14 (and finalized in Java 16), Records provide an immutable and compact way to represent data. Using records in Spring Data JPA allows better performance and cleaner code when fetching only required fields instead of full entity objects.

Benefits of Using Java Records in DTO Projections:

  • Immutable by Default: Ensures data integrity.
  • Concise Syntax: Reduces boilerplate code.
  • Optimized Performance: Fetches only necessary fields from the database.


2. Setting Up the Spring Boot Project

Dependencies

Ensure you have the necessary dependencies in your pom.xml for a Spring Boot application with Spring Data JPA.

<dependencies>
    <!-- Spring Boot Starter JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- H2 Database (for testing) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

3. Define the Entity Class

Let's say we have an Employee entity:

package com.example.demo.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "employees")
public class Employee {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String firstName;
    private String lastName;
    private String email;
    private double salary;

    // Default constructor
    public Employee() {}

    // Constructor with fields
    public Employee(String firstName, String lastName, String email, double salary) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.salary = salary;
    }

    // Getters and Setters (if needed)
}

4. Define a Java Record for DTO Projection

Instead of fetching the whole entity, we use a Java Record to fetch only specific fields.

package com.example.demo.dto;

public record EmployeeDTO(Long id, String firstName, String lastName, String email) {}

💡 Note: A record automatically generates a constructor and getter methods.


5. Create the Repository with DTO Projection

Spring Data JPA allows defining DTO projections using interfaces and now records:

package com.example.demo.repository;

import com.example.demo.dto.EmployeeDTO;
import com.example.demo.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;

public interface EmployeeRepository extends JpaRepository<Employee, Long> {

    // Projection Query Using Java Record DTO
    @Query("SELECT new com.example.demo.dto.EmployeeDTO(e.id, e.firstName, e.lastName, e.email) FROM Employee e")
    List<EmployeeDTO> findAllEmployees();

    // Fetch by last name
    @Query("SELECT new com.example.demo.dto.EmployeeDTO(e.id, e.firstName, e.lastName, e.email) FROM Employee e WHERE e.lastName = :lastName")
    List<EmployeeDTO> findByLastName(@Param("lastName") String lastName);
}

6. Implement the Service Layer

A service class encapsulates business logic:

package com.example.demo.service;

import com.example.demo.dto.EmployeeDTO;
import com.example.demo.repository.EmployeeRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmployeeService {
    
    private final EmployeeRepository employeeRepository;

    public EmployeeService(EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }

    public List<EmployeeDTO> getAllEmployees() {
        return employeeRepository.findAllEmployees();
    }

    public List<EmployeeDTO> getEmployeesByLastName(String lastName) {
        return employeeRepository.findByLastName(lastName);
    }
}

7. Create a REST Controller

Expose the service methods via a REST API:

package com.example.demo.controller;

import com.example.demo.dto.EmployeeDTO;
import com.example.demo.service.EmployeeService;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/employees")
public class EmployeeController {
    
    private final EmployeeService employeeService;

    public EmployeeController(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    @GetMapping
    public List<EmployeeDTO> getAllEmployees() {
        return employeeService.getAllEmployees();
    }

    @GetMapping("/lastname/{lastName}")
    public List<EmployeeDTO> getEmployeesByLastName(@PathVariable String lastName) {
        return employeeService.getEmployeesByLastName(lastName);
    }
}

8. Running the Application

  1. Run the Spring Boot application.
  2. Use Postman or a browser to test the API endpoints:
    • Get All Employees: http://localhost:8080/employees
    • Get Employees by Last Name: http://localhost:8080/employees/lastname/Smith

9. Performance Considerations

  • Reduced Memory Usage: DTO projections avoid fetching unnecessary columns, reducing memory consumption.
  • Optimized Queries: Spring Data JPA translates record projections into optimized SQL queries.

10. Conclusion

With Java Records, Spring Data JPA now provides a clean, immutable, and efficient way to work with DTO projections. This approach simplifies the code and improves performance.

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete