JPA/Querydsl

Dto로 프로젝션 결과 반환

chanhee01 2023. 8. 13. 14:55
@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);
    }
}

JPQL을 이용해서 Dto로 결과를 반환하려면 new 명령어를 사용해서 패키지명을 전부 다 입력해줘야지 Dto로 받아올 수 있었다.

 

 

 

Querydsl의 빈 생성

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

 

 

1. 프로퍼티 접근

@Test
public void findDtoBySetter() {
    List<MemberDto> result = queryFactory
            .select(Projections.bean(MemberDto.class, member.username, member.age))
            // Projections.bean(Dto 클래스, member.변수, member.변수)
            .from(member)
            .fetch();

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

프로퍼티 접근(bean)은 Dto에 있는 setter를 통해서 값이 생성되는 방식이다.

 

 

 

 

2. 필트 활용 방법

@Test
public void findDtoByField() {
    List<MemberDto> result = queryFactory
            .select(Projections.fields(MemberDto.class, member.username, member.age))
            // Projections.fields(Dto 클래스, member.변수, member.변수)
            .from(member)
            .fetch();

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

위에서 bean()을 fileds()로 바꿔준거 말고는 차이가 없는데 위에서는 setter를 이용해서 넣었는데 이번에는 그냥 필드에 주입시키는 것이다.

 

 

 

3. 생성자 사용

@Test
public void findDtoByConstructor() {
    List<MemberDto> result = queryFactory
            .select(Projections.constructor(MemberDto.class, member.username, member.age))
            // Projections.constructor(Dto 클래스, member.변수, member.변수)
            .from(member)
            .fetch();

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

construcotr는 생성자를 사용하는 것인데, MemberDto 클래스의 타입과 변수 순서가 일치해야 한다.

 

 

 

 

4. @QueryProjection

@Data
@NoArgsConstructor
public class MemberDto {

    private String username;
    private int age;

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

MemberDto의 생성자에 @QueryProjection을 넣어주면 QMemberDto가 생긴다.

별도의 QMemberDto가 생김

 

 

 

장점

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

이러한 방식의 장점은 생성자를 만들 때 필요한 것들만 주입할 수 있다는 것이다. new QMemberDto(member.username, member.age, member.key)라고 입력하면 컴파일 에러가 생긴다.

위의 경우에서는 이렇게 입력해도 런타임 에러가 생기는데, @QueryProjection은 컴파일 시점에 잡아준다는 장점이 있다.

 

 

 

 

 

단점

하지만 Q파일을 별도로 만들어야한다는 단점이 있고, Dto 계층에서 @QueryProjection을 사용하기 때문에 Dto에서도 querydsl의 기능이 들어가는 아키텍쳐 상 문제가 생긴다는 단점이 있다.

 

 

 

4가지 방식의 장단점을 판단해서 필요한 기능을 사용하면 된다.