Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Introduction to Spring Data Projections

Spring Data Projections provide a way to customize the query results by selecting only a subset of properties from the entities. This guide covers key concepts and steps for getting started with Spring Data Projections, including defining projections, using projections in repositories, and implementing both interface-based and class-based projections.

Key Concepts of Spring Data Projections

  • Projections: Mechanism to retrieve a subset of properties from an entity.
  • Interface-based Projections: Define projections using interfaces.
  • Class-based Projections: Define projections using classes.
  • DTO (Data Transfer Object): An object that carries data between processes.

Defining Interface-based Projections

Create an interface that defines the projection:

Example: UserProjection.java

// UserProjection.java
package com.example.myapp.projections;

public interface UserProjection {
    String getUsername();
    String getPassword();
}

Using Interface-based Projections in Repositories

Use the projection in your repository interface:

Example: UserRepository.java

// UserRepository.java
package com.example.myapp.repository;

import com.example.myapp.model.User;
import com.example.myapp.projections.UserProjection;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends JpaRepository {
    List findAllProjectedBy();
}

Defining Class-based Projections

Create a class that defines the projection:

Example: UserDTO.java

// UserDTO.java
package com.example.myapp.dto;

public class UserDTO {
    private String username;
    private String password;

    public UserDTO(String username, String password) {
        this.username = username;
        this.password = password;
    }

    // Getters and setters
}

Using Class-based Projections in Repositories

Use the projection in your repository interface:

Example: UserRepository.java

// UserRepository.java
package com.example.myapp.repository;

import com.example.myapp.model.User;
import com.example.myapp.dto.UserDTO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

@Repository
public interface UserRepository extends JpaRepository {
    
    @Query("SELECT new com.example.myapp.dto.UserDTO(u.username, u.password) FROM User u")
    List findAllUserDTOs();
}

Using Projections in the Service Layer

Use the projections in your service layer to retrieve and manipulate the data:

Example: UserService.java

// UserService.java
package com.example.myapp.service;

import com.example.myapp.dto.UserDTO;
import com.example.myapp.projections.UserProjection;
import com.example.myapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public List getAllUserProjections() {
        return userRepository.findAllProjectedBy();
    }

    public List getAllUserDTOs() {
        return userRepository.findAllUserDTOs();
    }
}

Testing Spring Data Projections

Test your Spring Data Projections setup to ensure it works as expected:

Example: UserServiceTests.java

// UserServiceTests.java
package com.example.myapp;

import com.example.myapp.dto.UserDTO;
import com.example.myapp.projections.UserProjection;
import com.example.myapp.repository.UserRepository;
import com.example.myapp.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@SpringBootTest
public class UserServiceTests {

    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetAllUserProjections() {
        UserProjection projection = new UserProjection() {
            @Override
            public String getUsername() {
                return "testuser";
            }

            @Override
            public String getPassword() {
                return "password";
            }
        };

        when(userRepository.findAllProjectedBy()).thenReturn(List.of(projection));

        List projections = userService.getAllUserProjections();

        assertThat(projections).isNotEmpty();
        assertThat(projections.get(0).getUsername()).isEqualTo("testuser");
    }

    @Test
    public void testGetAllUserDTOs() {
        UserDTO dto = new UserDTO("testuser", "password");

        when(userRepository.findAllUserDTOs()).thenReturn(List.of(dto));

        List dtos = userService.getAllUserDTOs();

        assertThat(dtos).isNotEmpty();
        assertThat(dtos.get(0).getUsername()).isEqualTo("testuser");
    }
}

Key Points

  • Projections: Mechanism to retrieve a subset of properties from an entity.
  • Interface-based Projections: Define projections using interfaces.
  • Class-based Projections: Define projections using classes.
  • DTO (Data Transfer Object): An object that carries data between processes.
  • Create an interface that defines the projection for interface-based projections.
  • Use the projection in your repository interface for interface-based projections.
  • Create a class that defines the projection for class-based projections.
  • Use the projection in your repository interface for class-based projections.
  • Use the projections in your service layer to retrieve and manipulate the data.
  • Test your Spring Data Projections setup to ensure it works as expected.

Conclusion

Spring Data Projections provide a way to customize the query results by selecting only a subset of properties from the entities. By understanding and implementing interface-based and class-based projections, you can effectively manage data in your Spring Boot application. Happy coding!