흰 스타렉스에서 내가 내리지

[Querydsl] 프로젝션 결과 반환 - DTO 조회 본문

JPA

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

주씨. 2024. 4. 19. 05:37
728x90

# 1. JPQL 을 이용한 DTO 조회

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberDto {
    private String username;
    private int age;

}

 

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

 

  • 순수 JPA 에서 DTO 를 조회할 때는 new 명령어를 사용해야 함
  • DTO 의 package 이름을 다 적어줘야 해서 지저분하다.
  • 생성자 방식만 지원한다.

 

# Querydsl 빈 생성 

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

1. 프로퍼티 접근

2. 필드 직접 접근

3. 생성자 사용

 

 

# 1. 프로퍼티 접근 - Setter

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

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

 

 

# 2.필드 직접 접근

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

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

 

 

 

++ 별칭이 다를 때

@Data
public class UserDto {
    private String name;
    private int age;
}

 

 

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

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

    /**
     * memberDto = UserDto(name=null, age=10)
     * memberDto = UserDto(name=null, age=20)
     * memberDto = UserDto(name=null, age=30)
     * memberDto = UserDto(name=null, age=40)
     */
}
  • 일치하는 필드를 찾지 못해 name 에 Null 값이 들어갔다.

 

@Test
public void findUserDto2(){
    List<UserDto> resultList = queryFactory
            .select(Projections.fields(UserDto.class, member.username.as("name"), member.age))
            .from(member)
            .fetch();

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

    /**
     * memberDto = UserDto(name=member1, age=10)
     * memberDto = UserDto(name=member2, age=20)
     * memberDto = UserDto(name=member3, age=30)
     * memberDto = UserDto(name=member4, age=40)
     */
}

 

  • 프로퍼티나, 필드 접근 생성 방식에서 이름이 다를 때 해결 방안이다. 
  • ExpressionUtils.as(source, alias) : 필드나, 서브 쿼리에 별칭 적용
  • username.as("memberName") : 필드에 별칭 적용

 

# 3. 생성자 사용

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

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

 

 

 

# 프로젝션 결과 반환 - @QuertProjection

생성자 + @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;
    }
}

 

  • compile 후 QMemberDto 생성확인

 

@QueryProjection 활용

List<MemberDto> resultList = queryFactory
        .select(new QMemberDto(member.username, member.age))
        .from(member)
        .fetch();

 

  • 이 방법은 컴파일러로 타입을 체크할 수 있으므로 가장 안전한 방법이다. 
  • 다만 MemberDTO 가 QueryDSL 어노테이션을 가짐으로써 의존성을 가지게 된다는 단점이 있다
  • 또한, DTO 까지 Q 파일을 생성한다는 단점이 있다. 

'JPA' 카테고리의 다른 글

[Querydsl] 동적 쿼리 1 - BooleanBuilder 사용  (0) 2024.04.20
[Querydsl] distinct  (0) 2024.04.19
[Querydsl] 서브쿼리  (0) 2024.04.19
[Querydsl] 조인  (0) 2024.04.18
[Querydsl] 집합 함수, GroupBy  (0) 2024.04.18