Study/JPA

[QueryDSL] select 절에서 조회 대상 지정 방법

오늘만 사는 여자 2022. 6. 16. 17:06
728x90
반응형

QueryDSL을 이용하여 entity 전체를 가져오는 방법 말고, 조회 대상을 지정하여 원하는 값만 조회하는 것을 프로젝션이라고 합니다.

 

 프로젝션 대상이 하나일 경우에는 반환되는 타입이 프로젝션 대상의 타입입니다.

    @Test
    public void oneProjectionTest() {
        QMember m = QMember.member;

        query.select(m.name)
                .from(m)
                .fetch()
                .stream()
                .forEach(name -> log.info("name is : " + name));
    }

위의 결과를 보면 Member 엔티티의 name은 String 타입이므로 프로젝션 대상이 하나일 때, List<String> 타입이 조회 결과로 반환되는 것을 볼 수 있습니다.


 만약 두 개 이상일 경우에는 어떤 타입이 반환될까요??

    @Test
    public void twoProjectionsTest() {
        QMember m = QMember.member;

        List<Tuple> results = query.select(m.name, m.age)
                .where(m.age.goe(30L))
                .from(m)
                .fetch();

        results.stream()
                .forEach(tuple -> {
                    log.info("name is " + tuple.get(0, String.class));
                    log.info("age is " + tuple.get(m.age));
                });
    }

 프로젝션 대상이 둘 이상이라면 Tuple 타입을 반환합니다. Tuple을 조회할 때는 get() 메서드를 이용하면 됩니다.

사용해보니 get() 으로 조회하는 방법으로 두 가지가 있는 것 같습니다.

 

첫 번째 방법은 get() 메서드의 첫 번째 파라미터로 프로젝션 대상의 순번, 두 번째 파라미터는 해당 값의 타입을 명시하는 방법입니다.

위의 예제 상에서 첫 번째 로그를 찍는 tuple.get(0, String.class) 코드입니다. select 대상에서 첫 번째 값으로 String 타입인 m.name 으로 지정하였으므로 0번 째 조회 결과를 가져오는 것입니다.

 

 두 번째 방법은 간단하게 조회한 쿼리 타입을 바로 지정하는 것입니다.

로그를 찍는 코드상에서 tuple.get(m.age) 에 해당하는 방법입니다.

 

 저는 두 가지 방법 중에서 두 번째 방법이 더 가독성이 있어 보입니다.

 


 

 쿼리의 결과를 특정 객체로 받고 싶을 때는 QueryDSL에서 제공하는 Projections 클래스를 이용하면 됩니다.

 

Projections 클래스를 이용하여 객체를 생성하는 방법 3가지

  • 프로퍼티 접근
  • 필드 직접 접근
  • 생성자 사용
package com.edu.querydsl_training.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO {

    private String name;
    private Long age;
}

 

예제를 위해 MemberDTO 클래스를 생성하였습니다. 해당 클래스는 단순하게 Member의 이름과 나이를 담는 DTO클래스입니다.

 


 

 먼저 프로퍼티를 이용한 접근 방법을 살펴보겠습니다.

프로퍼티 접근 방법은 Projections.bean() 메서드를 이용하여 객체를 생성할 수 있고, setter() 메서드를 사용해서 값을 주입시켜줍니다.

    @Test
    public void simpleProjectionTest() {
        QMember m = QMember.member;

        query.select(Projections.bean(MemberDTO.class, m.name, m.age.as("age")))
                .from(m)
                .fetch()
                .stream()
                .forEach(memberDTO -> {
                    log.info("member name : " + memberDTO.getName());
                    log.info("member age : " + memberDTO.getAge());
                });
    }

 

 

필드 직접 접근 방법

Projections.fields() 메서드를 이용하여 값을 주입시킵니다. 필드의 접근 지정자를 private로 지정해도 동작하며, setter() 메서드가 없어도 정상적으로 값이 주입됩니다.

    @Test
    public void simpleProjectionTest_2() {
        QMember m = QMember.member;

        query.select(Projections.fields(MemberDTO.class, m.name, m.age))
                .from(m)
                .fetch()
                .stream()
                .forEach(memberDTO -> {
                    log.info("member name : " + memberDTO.getName());
                    log.info("member age : " + memberDTO.getAge());
                });
    }

 

생성자를 이용하는 방법

Projections.constructor() 메서드를 이용하면 됩니다. 해당 메서드는 생성자를 이용하여 객체에 값을 주입하며, 지정한 프로젝션과 생성자의 파라미터 순서가 같아야 합니다.

    @Test
    public void simpleProjectionTest_3() {
        QMember m = QMember.member;

        query.select(Projections.constructor(MemberDTO.class, m.name, m.age))
                .from(m)
                .fetch()
                .stream()
                .forEach(value -> {
                    log.info("member name : " + value.getName());
                    log.info("member age : " + value.getAge());
                });
    }

 

출처 : https://icarus8050.tistory.com/5

728x90
반응형