본문 바로가기
Study/JPA

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

by 오늘만 사는 여자 2022. 6. 16.
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
반응형

댓글