ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Querydsl - DTO로 조회하기
    Spring/Querydsl 2022. 3. 25. 12:15
    728x90
    반응형

     

     

    순수 JPA에서의 DTO 조회

    • new 명령어 사용해야 한다.
    • DTO의 package 이름을 다 적어줘야하는 불편함이 있다.
    • 생성자 방식만 지원한다.

     

    List<MemberDto> result = em.createQuery(
            "select new study.querydsl.dto.MemberDto(m.username, m.age) " +
                    "from Member m", MemberDto.class)
            .getResultList();

     

     위와 같은 순수 JPA에서의 DTO 조회 시 사용하기 불편했던 것들을 Querydsl에서 해결해준다.

     


    DTO로 조회하기

    • 프로퍼티 접근 - setter
    • 필드 직접 접근
    • 생성자 사용
    • @QueryProjection

     

    프로퍼티 접근 - setter

     

    • Projections.bean(class, {field, ...}) 사용
    • DTO의 getterm setter가 필요하다.

     

    @Test
    @DisplayName("DTO 조회 - 프로퍼티 접근")
    public void dtoProperty() {
        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);
        }
    }
        /* select
            member1.username,
            member1.age 
        from
            Member member1 */ select
                member0_.username as col_0_0_,
                member0_.age as col_1_0_ 
            from
                member member0_
    memberDto = MemberDto(username=member1, age=10)
    memberDto = MemberDto(username=member2, age=20)
    memberDto = MemberDto(username=member3, age=30)
    memberDto = MemberDto(username=member4, age=40)

     

     Tuple을 사용하지 않고 DTO를 이용하여 필요한 정보만 가져온 것을 볼 수 있다.


     

    필드 직접 접근

     

    • Projections.fields(class, {field, ...}) 사용
    • DTO의 getter, setter가 없어도 사용 가능

     

    @Test
    @DisplayName("DTO 조회 - 필드 접근")
    public void dtoField() {
        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);
        }
    }
        /* select
            member1.username,
            member1.age 
        from
            Member member1 */ select
                member0_.username as col_0_0_,
                member0_.age as col_1_0_ 
            from
                member member0_
    memberDto = MemberDto(username=member1, age=10)
    memberDto = MemberDto(username=member2, age=20)
    memberDto = MemberDto(username=member3, age=30)
    memberDto = MemberDto(username=member4, age=40)

     

     필드명이 다른 경우에는 다음과 같이 별칭(alias)를 사용하면 된다. 필드명과 데이터명이 다를 경우 필드에는 null이 들어가니 조심하자.

    @Data
    public class UserDto {
        private String name;
        private int age;
    }
    
    List<UserDto> fetch = queryFactory
            .select(Projections.fields(UserDto.class,
                    member.username.as("name"),	// (1)
                    ExpressionUtils.as(		// (2)
                            JPAExpressions
                                    .select(memberSub.age.max())
                                    .from(memberSub), "age")
                    )
            ).from(member)
            .fetch();
    • 프로퍼티나, 필드 접근 생성 방식에서 이름이 다를 때 해결 방안
    • (1) : username.as("memberName") : 필드에 별칭 적용
    • (2) : ExpressionUtils.as(source, alias) : 필드나 서브 쿼리에 별칭 적용

     

     


     

    생성자 사용

     

    • Projections.constructor(class, {field, ...}) 사용
    • 필드와 타입이 일치해야 한다.

     

    @Test
    @DisplayName("DTO 조회 - 생성자 접근")
    public void dtoConstruct() {
        List<Member> results = queryFactory
                .select(Projections.constructor(Member.class,
                        member.username,
                        member.age))
                .from(member)
                .fetch();
    
        for (Member result : results) {
            System.out.println("result = " + result);
        }
    }
        /* select
            member1.username,
            member1.age 
        from
            Member member1 */ select
                member0_.username as col_0_0_,
                member0_.age as col_1_0_ 
            from
                member member0_
    memberDto = MemberDto(username=member1, age=10)
    memberDto = MemberDto(username=member2, age=20)
    memberDto = MemberDto(username=member3, age=30)
    memberDto = MemberDto(username=member4, age=40)

     

    @QueryProjection

     

    • 생성자에 @QueryProjection을 사용하면 Q-Type이 생성된다.
    • 컴파일러로 타입을 체크할 수 있어 가장 안전한 방법(ctrl+p 로 파라미터 확인 가능)
    • DTO까지 Q 파일을 생성해야하는 단점
    • DTO가 Querydsl에 대한 의존성을 갖게 된다.

     

    @Data
    public class MemberDto {
    
        private String username;
        private int age;
    
        public MemberDto() {
        }
    
        @QueryProjection	// (1)
        public MemberDto(String username, int age) {
            this.username = username;
            this.age = age;
        }
    }
    • (1) : QMemberDto 만들어 주는 어노테이션

     

    @Test
    @DisplayName("QueryProjection")
    public void queryProjection() {
    
        List<MemberDto> results = queryFactory
                .select(new QMemberDto(member.username, member.age))
                .from(member)
                .fetch();
    
        for (MemberDto result : results) {
            System.out.println("result = " + result);
        }
    }
        /* select
            member1.username,
            member1.age 
        from
            Member member1 */ select
                member0_.username as col_0_0_,
                member0_.age as col_1_0_ 
            from
                member member0_
    result = MemberDto(username=member1, age=10)
    result = MemberDto(username=member2, age=20)
    result = MemberDto(username=member3, age=30)
    result = MemberDto(username=member4, age=40)

     

     

    728x90
    반응형

    댓글

Designed by Tistory.