[QueryDSL] 중급 문법

윤경·2021년 12월 7일
0

QueryDSL

목록 보기
6/11
post-thumbnail

[1] 프로젝션과 결과 반환 - 기본

프로젝션: select 대상 지정
(즉, select 절에 뭘 가져올지 대상을 지정하는 것)

프로젝션 대상이 하나일 때

프로젝션 대상이 하나일 때는 그냥 그 대상에 맞춰서 반환 타입을 주면 된다.

➡️ 프로젝션 대상이 하나면 타입을 명확하게 지정할 수 있음

프로젝션 대상이 둘 이상일 때

프로젝션 대상이 둘 이상이라면 튜플이나 DTO로 조회해야 한다.

  • DTO: 자바 빈을 만들고 거기에 여러 데이터를 받을 수 있도록 자료구조를 만들어 놓은 것
  • Tuple: 한 번에 여러개를 담아 막 꺼낼 수 있음
    (com.querydsl.core.Tuple)

⚠️ 튜플은 리포지토리 안에서 필요할 때만 사용하고 그 이외에는 DTO를 사용하는 것을 추천함


[2] 프로젝션과 결과 반환 - DTO 조회

: 순수 JPA에서 DTO 조회하기

먼저 MemberDto를 생성

@Data
@NoArgsConstructor  // 기본 생성자 대신
public class MemberDto {

    private String username;
    private int age;

//    public MemberDto() {  // @NoArgsConstructor로 대신할 수 있음
//    }

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

순수 JPA에서 DTO 조회

        List<MemberDto> result = em.createQuery("select new study.Querydsl.dto.MemberDto(m.username, m.age) " +
                        "from Member m", MemberDto.class)
                .getResultList();
  • 순수 JPA에서 DTO를 조회할 때는 new 명령어를 사용해야 한다.
  • DTO의 package(패키지) 이름을 다 적어줘야 해서 코드가 지저분하다.
  • 생성자 방식만을 지원한다.

Querydsl 빈 생성 (Bean population)

결과를 DTO 반환할 때 사용한다.

그리고

  • 프로퍼티 접근
  • 필드 직접 접근
  • 생성자 사용

이렇게 세 가지 방법을 지원한다.

1. 프로퍼티 접근 - Setter

        List<MemberDto> result = queryFactory
                .select(Projections.bean(MemberDto.class,   // lombok -> getter, setter를 통한
                        member.username,
                        member.age))
                .from(member)
                .fetch();
  • bean: setter로 인젝션 해줌
  • (bean()속) MemberDto.class: 타입을 지정
  • member.username, member.age: 타입 지정 다음 꺼내올 값들을 나열하기

2. 필드 직접 접근

        List<MemberDto> result = queryFactory
                .select(Projections.fields(MemberDto.class, // getter, setter를 무시하고 field에 값을 바로 꽂아버림
                        member.username,
                        member.age))
                .from(member)
                .fetch();
  • bean 대신 fields로 바꿔주면 됨

혹시 별칭이 다를 때 (as)

: 프로퍼티나 필드 접근 생성 방식에서 이름이 다를 때 해결 방안이다.

(UserDto에는 username이 아닌 name으로 해놨음)

        List<UserDto> result = queryFactory
                .select(Projections.fields(UserDto.class,
                        member.username.as("name"),
                        member.age))
                .from(member)
                .fetch();
  • ExpressionUtils.as(source, alias): 필드나, 서브 쿼리 별칭 적용
  • username.as("memberName"): 필드에 별칭 적용

4. 생성자 사용

        List<MemberDto> result = queryFactory
                .select(Projections.constructor(MemberDto.class,    // constructor는 타입을 맞춰야함
                        member.username,
                        member.age))
                .from(member)
                .fetch();

[3] 프로젝션과 결과 반환 - @QueryProjection

: 제일 깔끔한 궁극의 방법이지만 단점이 있음

(생성자를 사용하는 방식에서는 @QueryProjections도 지원한다.)

DTO를 Q파일로 생성시키기 위해서는

이 방법은 컴파일러로 타입을 체크할 수 있어 가장 안전하다.

다만, DTO에 QueryDSL 어노테이션을 유지해야 하는 것과 DTO까지 Q파일을 생성해야 하는 단점이 있다.
➡️ memberDto가 QueryDSL에 의존성을 가지게 되어버린다.

생성자 사용하는 방법이랑 차이점: 생성자 사용하는 방법은 컴파일 시점에야 에러를 잡을 수 있지만, @QueryProjection은 코드 작성시 에러를 바로 알 수 있음


profile
개발 바보 이사 중

0개의 댓글