본문 바로가기
Java & Kotlin/Spring Data

[QueryDSL] 프로젝션

by heekng 2022. 5. 14.
반응형

프로젝션

QueryDSL에서 프로젝션을 사용하는 방법을 알아보자.
프로젝션: select 절에 조회 대상을 지정하는 것 ex) Dto로 조회

기본

List<String> result = queryFactory
    .select(member.username)
    .from(member)
    .fetch();
  • 프로젝션: select 대상 지정
  • 프로젝션 대상이 하나면 리턴되는 타입이 해당 타입으로 반환된다.

튜플 조회

List<Tuple> result = queryFactory
    .select(member.username, member.age)
    .from(member)
    .fetch();
  • 여러 컬럼을 조회할 때에는 com.mysema.query.Tuple 타입을 사용해서 반환된다.
  • 조회 결과는 result.get(member.username)과 같은 형태로 받아온다.

DTO 조회

Dto를 이용해 조회하는 경우는 프로퍼티 접근, 필드 직접 접근, 생성자 사용 방식이 있다.

프로퍼티 접근

List<MemberDto> result = queryFactory
    .select(Projections.bean(MemberDto.class, member.username, member.age))
    .from(member)
    .fetch();
  • 프로퍼티 접근 방식은 Projections.bean(dto클래스, 파라미터...)을 이용한다.
  • 내부적으로 new DTO()하여 객체를 생성하고, setter메서드를 이용해 필드를 넣어준다.
  • 때문에 기본 생성자setter메서드가 꼭 필요하다.

필드 직접 접근

List<MemberDto> result = queryFactory
    .select(Projections.fields(MemberDto.class, member.username, member.age))
    .from(member)
    .fetch();
  • 필드 직접 접근 방식은 Projection.fields(dto클래스, 파라미터...)을 이용한다.
  • 프로퍼티 접근 방식과 다르게 내부적으로 객체를 생성하면서 파라미터를 바로 넣어주기 때문에 setter메서드가 필요하지 않다.
  • 필드를 private로 설정해도 동작한다.

생성자 사용

List<MemberDto> result = queryFactory
    .select(Projections.constructor(MemberDto.class, member.username, member.age))
    .from(member)
    .fetch();
  • 생성자 사용 방식은 Projections.constructor(dto클래스, 파라미터...)을 이용하며, 말 그대로 생성자를 이용한다.
  • 생성자를 사용하기 때문에 파라미터를 받아줄 생성자가 필요하며, 파라미터 순서도 생성자의 파라미터 순서와 일치해야 한다.

alias 사용 시 - 프로퍼티 접근, 필드 직접 접근

QMember memberSub = new QMember("memberSub");
    List<UserDto> result = queryFactory
        .select(Projections.fields(UserDto.class,
                member.username.as("name"),
                ExpressionUtils.as(
                        JPAExpressions
                                .select(memberSub.age.max())
                                .from(memberSub)
                        , "age"
                )
        ))
        .from(member)
        .fetch();
  • 프로퍼티 접근 방식이나 필드 직접 접근 방식일 때에는 필드명과 컬럼명을 일치시켜주어야 한다.
  • dto의 필드명이 엔티티 필드명과 다를 때에는 alias를 사용해야한다.
  • member.username.as("name")와 같이 필드에 직접 alias를 설정한다.
  • 필드나 서브쿼리에 별칭을 사용하는 ExpressionUtils.as(source, alias)를 이용하기도 한다.

@QueryProjection

@QueryProjection
public MemberDto(String username, int age) {
    this.username = username;
    this.age = age;
}

...

List<MemberDto> result = queryFactory
    .select(new QMemberDto(member.username, member.age))
    .from(member)
    .fetch();
  • dto클래스를 QType로 생성해 사용하는 방식이다.
  • 컴파일 시점에서 에러를 잡아낼 수 있다는 강력한 장점이 있다.
  • 하지만 QType을 생성해야 하는 단점이 있는데
    이는 DTO클래스 내부에 QueryDSL 어노테이션이 포함되어 의존하는 문제가 생긴다.
반응형