Spring Data JPA Specific Columns using JPQL and DTO Projections

Selecting specific columns (or a subset of columns) in Spring Data JPA can be achieved using multiple ways, but one of the most common approaches is using JPQL in combination with DTO projections. In this guide, I'll demonstrate how to select specific columns using this approach.

1. Setting up the project

Make sure you have the required dependencies in your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2. Define the Entity

For this example, let's use a Person entity:
@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    // getters, setters, etc.
}

3. Create a DTO Projection

This DTO will represent the subset of columns we wish to retrieve. For instance, if we only want the firstName and lastName:
public class PersonNameDto {
    private final String firstName;
    private final String lastName;

    public PersonNameDto(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // getters
}
Note the final keyword for fields and the constructor to initialize these fields. 

4. Create the Repository

Define a custom query using JPQL in the repository:
public interface PersonRepository extends JpaRepository<Person, Long> {

    @Query("SELECT new com.yourpackage.PersonNameDto(p.firstName, p.lastName) FROM Person p")
    List<PersonNameDto> findAllNames();
}
The above query selects only the firstName and lastName from the Person entity and maps them to the PersonNameDto

5. Use the Repository in a Service

Now, you can retrieve the specific columns in your service layer:
@Service
public class PersonService {

    @Autowired
    private PersonRepository personRepository;

    public List<PersonNameDto> getPersonNames() {
        return personRepository.findAllNames();
    }
}

6. Test the Service

You can create a test to ensure the specific columns are being retrieved:
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private PersonService personService;

    @Test
    public void testFetchNames() {
        List<PersonNameDto> names = personService.getPersonNames();
        for (PersonNameDto name : names) {
            assertNotNull(name.getFirstName());
            assertNotNull(name.getLastName());
        }
    }
}

Notes

This approach uses DTO projections to map specific columns, providing a type-safe way of retrieving partial entities. 

This is useful for performance, especially when the full entity contains many columns or large content (like blobs). It's also possible to achieve similar functionality using native SQL queries, the JPA Criteria API, or other third-party libraries like Querydsl. 

 Always ensure that the DTO's constructor parameters match the order of columns in your custom query to ensure the correct mapping.

Comments