Querydsl - 중급 문법(1)

YulHee Kim·2022년 1월 10일
0

Querydsl

목록 보기
5/6

김영한님의 '실전! Querydsl'을 수강하며 정리하는 글입니다

중급 문법

✔️ 프로젝션과 결과 반환 - 기본

프로젝션이란 엔티티에 속성이 너무 많을 때 일부 데이터만 가져오는 방법입니다. 즉, select 쿼리에서 일부 대상만 가져오는 것 입니다.

프로젝션 대상이 하나

    @Test
    public void simpleProjection() {
        List<String> result = queryFactory
                .select(member.username)
                .from(member)
                .fetch();

        for (String s : result) {
            System.out.println("s = " + s);
        }
    }
  • 프로젝션 대상이 하나면 타입을 명확하게 지정할 수 있습니다
  • 프로젝션 대상이 둘 이상이면 튜플이나 DTO로 조회합니다

튜플 조회

프로젝션 대상이 둘 이상일 때 사용
com.querydsl.core.Typle

    @Test
    public void tupleProjection() {
        List<Tuple> result = queryFactory
                .select(member.username, member.age)
                .from(member)
                .fetch();

        for (Tuple tuple : result) {
            String username = tuple.get(member.username);
            Integer age = tuple.get(member.age);
            System.out.println("username = " + username);
            System.out.println("age = " + age);
        }
    }

✔️ 프로젝션과 결과 반환 - DTO 조회

순수 JPA에서의 DTO 조회

MemberDto

@Data
@NoArgsConstructor
public class MemberDto {

    private String username;
    private int age;

}

순수 JPA에서 DTO조회 코드

    @Test
    public void findDtoByJPQL() {
        List<MemberDto> result = em.createQuery("select new study.querydsl.dto.MemberDto(m.username, m.age) from Member m", MemberDto.class)
                .getResultList();

        for (MemberDto memberDto : result) {
            System.out.println("memberDto = " + memberDto);
        }
    }
  • 순수 JPA에서 DTO를 조회할 때는 new 명령어를 사용해야합니다
  • DTO의 package 이름을 다 적어줘야해서 지저분합니다.
  • 생성자 방식만 지원합니다.

Querydsl 빈 생성(Bean population)

결과를 DTO로 반환할 때 사용하며, 다음 3가지 방법을 지원합니다.

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

프로퍼티 접근 - Setter

    @Test
    public void findDtoBySetter() {
        List<MemberDto> result = queryFactory
                .select(Projections.bean(MemberDto.class,
                        member.username,
                        member.age))
                .from(member)
                .fetch();
        for (MemberDto memberDto : result) {
            System.out.println("memberDto = " + memberDto);
        }
    }

필드 직접 접근

    @Test
    public void findDtoByField() {
        List<MemberDto> result = queryFactory
                .select(Projections.fields(MemberDto.class,
                        member.username,
                        member.age))
                .from(member)
                .fetch();

        for (MemberDto memberDto : result) {
            System.out.println("memberDto = " + memberDto);
        }
    }

별칭이 다를 때

@Data
public class UserDto {
    private String name;
    private int age;
}
    @Test
    public void findUserDto() {
        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();

        for (UserDto userDto : result) {
            System.out.println("userDto = " + userDto);
        }
    }

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

  • ExpressionUtils.as(source,alias): 필드나, 서브 쿼리에 별칭을 적용합니다.
  • username.as("memberName"): 필드에 별칭을 적용합니다.
    username.as()방식이 간편하지만 서브쿼리를 사용하려면 전자의 방식을 사용해야합니다.

생성자 사용

    @Test
    public void findDtoByConstructor() {
        List<UserDto> result = queryFactory
                .select(Projections.constructor(UserDto.class,
                        member.username,
                        member.age))
                .from(member)
                .fetch();

        for (UserDto memberDto : result) {
            System.out.println("memberDto = " + memberDto);
        }
    }

✔️ 프로젝션 결과 반환 - @QueryProjection

생성자 + @QueryProjection

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

gradle을 열어 compileQuerydsl을 두번 클릭해주면 QMemberDto가 생성된 것을 확인할 수 있습니다.

@QueryProjection 활용

    @Test
    public void findDtoByQueryProjection() {
        List<MemberDto> result = queryFactory
                .select(new QMemberDto(member.username, member.age))
                .from(member)
                .fetch();

        for (MemberDto memberDto : result) {
            System.out.println("memberDto = " + memberDto);
        }
    }

이 방식은 컴파일러로 타입을 체크할 수 있어서 가장 안전하고 편한 방법이지만 DTO에 Querydsl 어노테이션을 유지해야 한다는 점에서 querydsl 라이브러리에 의존적인 단점이있꼬, DTO까지 Q파일을 생성해야 하는 단점이 있습니다.

profile
백엔드 개발자

0개의 댓글