본문 바로가기

CS 잡지식

Querydsl 사용 시, 주의해야 할 버그(newInstance, ExpressException..)

@Test // [Property 접근법] : getter를 사용하여 property에 접근하고, setter를 이용하여 값을 삽입
void findDtoBySetter(){

    List<MemberDto> results = queryFactory
            .select(Projections.bean(MemberDto.class,
                    member.username,
                    member.age))
            .from(member)
            .fetch();

    for (MemberDto result : results) {
        System.out.println("result = " + result);
    }
@Data
public class MemberDto {

    private String username;
    private int age;

  

    public MemberDto(String username,int age){

        this.username = username;
        this.age = age;

    }



}

 

JPQL에서 new 연산자를 통해서 바로 Dto로 조회를 하는 기능을 QueryDsl도 제공을 한다. 

그런데, 위 코드를 돌리게 되면 아래와 같은 에러가 난다. 

즉, 객체를 생성하지 못하는 에러가 발생을 하고 있다. 

@Data는 @RequiredConstructor이라는 기능을 지원한다. 

그런데 이 에노테이션은 final 키워드가 붙은 키워드에 대해서만 생성자를 만들어 준다. 

[기본 생성자]에 대해서는 생성을 해 주지 않는다. 

Querydsl에서 new 기능을 사용하기 위해서는 Dto에 [기본 생성자]가 꼭 있어야 한다. 

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

    private String username;
    private int age;

    //
    //ublic MembberDto()
    //{}

    public MemberDto(String username,int age){

        this.username = username;
        this.age = age;

    }



}

 

또 다른 에러의 종류를 살펴 보자. 

@Data
public class UserDto {

    private String name;
    private int age;


}

 

@Test // [필드 직접 접근] : 이건 getter, setter 없이, 필드에 바로 값을 꽂음.
void findDtoByField(){

    List<MemberDto> results = queryFactory
            .select(Projections.fields(UserDto.class, // Dto가 UserDto로 바뀜. 
                    member.username, // UserDto에는 username이 없다. 
                    member.age))
            .from(member)
            .fetch();

 

UserDto의 필드 중, member."username"과 매칭하는 필드명이 없으므로, null로 들어 간다. 

필드명이 불일치할 경우, 아래와 같이 as를 붙여 alias를 붙여 줘야 한다. 

@Test
void findUserDto(){

    List<UserDto> results = queryFactory
            .select(Projections.fields(UserDto.class, // Dto가 MemberDto에서 UserDto로 바뀜.
                    member.username.[as("name")], // UserDto의 필드에는 [username]이라는 필드명이 없다.(결과를 출력하면, name = [null]로 뜰 것이다.)
                    member.age))
            .from(member)
            .fetch();

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

 

@Test // 서브 쿼리에 alias를 설정하여, UserDto의 필드에 서브쿼리의 결과 값을 삽입 가능하다. 
void findUserDtoSubQuery(){

    QMember memberSub = new QMember("memberSub");


    List<UserDto> results = queryFactory
            .select(Projections.fields(UserDto.class, 
                    
                    member.username.as("name"), 
                    
                    //서브 쿼리의 결과를 UserDto의 "name"필드에 삽입
                    ExpressionUtils
                            .as(
                            JPAExpressions
                            .select(memberSub.age.max())
                            .from(memberSub),"age") // 서브 쿼리의 결과값의 alias를 "age"로 줘서, UserDto::name에 삽입
                    
                    ))                    
            .from(member)
            .fetch();

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

 

꼭 서브 쿼리가 아니더라도, 사용이 가능하다. 

예를 들어, member.username.as("name") == ExpressionUtils.as(member.username, "username")

'CS 잡지식' 카테고리의 다른 글

연관 관계 세팅 TIP!!!  (0) 2023.05.19
샘플 데이터 입력의 2가지 방법  (0) 2023.05.19
use_sql_comments: true  (0) 2023.05.17
Querydsl 사용 템플릿  (0) 2023.05.17
EntityManager에 대한 동시성(concurrency) 문제  (0) 2023.05.17