개발/JPA

[Querydsl] 검색 쿼리를 Querydsl로 구현하기(3)

highright96 2021. 7. 16.

프로젝션

프로젝션이란 select 대상을 지정하는 것을 말한다.

 

프로젝션 대상이 하나일 경우

List<String> result = queryFactory
        .select(member.username)
        .from(member)
        .fetch();

 

프로젝션 대상이 둘 이상일 경우

튜플 혹은 DTO를 사용해 조회한다.

 

 

튜플 조회

List<Tuple> result = queryFactory
        .select(member.username, member.age)
        .from(member)
        .fetch();

 

DTO 조회

Querydsl에서 DTO를 직접 반환할 수 있는 방법은 4가지가 있다.

 

1) 프로퍼티 접근

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

 

2) 필드 직접 접근

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

 

3) 생성자 사용

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

 

4) @QueryProjection 사용

DTO 클래스의 생성자에 아래와 같이 @QueryProjection을 적어둔다.

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

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

 

주의할 점은 아래 명령어를 사용해서 QMemberDto를 생성해야 한다.

 

./gradlew compileQuerydsl

 

이 방법이 컴파일러 전에 타입 체크를 할 수 있어 가장 안전한 방법이다. 그러나 DTO에 Querydsl 어노테이션을 사용하는 점과 Q 클래스를 생성해야 하는 단점이 있다.

 

개인적인 생각으로는 극한의 성능을 추구하는 것이 아니라면 엔티티를 조회한 후 DTO로 직접 반환하는 것이 좋아 보인다.

 

동적 쿼리

Querydsl에서 동적 쿼리를 다음과 같이 두 가지 방식을 지원해준다.

 

BooleanBuilder 방식

String usernameParam = "member1";
Integer ageParam = null;

BooleanBuilder builder = new BooleanBuilder();

if (usernameParam != null) {
	builder.and(member.username.eq(usernameParam));
}
if (ageParam != null) {
	builder.and(member.age.eq(ageParam));
}

List<Member> result = queryFactory
        .selectFrom(member)
        .where(builder)
        .fetch();

 

파라미터의 NULL값을 체크한 뒤 BooleanBuild를 사용해 where 절을 생성해준다.

하지만 이 방법은 직접 NULL값을 체크해야 하는 단점이 있다. 파라미터가 많으면 코드가 매우 길어진다. 이 방법을 해결하기 위해 Querydsl은 Where 다중 파라미터 방식을 지원해준다.

 

Where 다중 파라미터 방식

private List<Member> searchMember2(String usernameParam, Integer ageParam) {
        return queryFactory
            .selectFrom(member)
            .where(usernameEq(usernameParam), ageEq(ageParam))
            .fetch();
}

private BooleanExpression usernameEq(String usernameParam) {
	return usernameParam != null ? member.username.eq(usernameParam) : null;
}

private BooleanExpression ageEq(Integer ageParam) {
	return ageParam != null ? member.age.eq(ageParam) : null;
}

 

Where 절의 조건에 NULL값은 무시된다. 따라서 파라미터마다 삼항 연산자로 NULL값을 체크하는 메서드를 생성해 Where 절에서 호출할 수 있다. 쿼리의 코드가 간결해지고 가독성이 높아진다.

 

메서드를 다른 쿼리에서 사용할 수 있고, 아래와 같이 조합해서 활용할 수 있다.

 

private List<Member> searchMember2(String usernameParam, Integer ageParam) {
        return queryFactory
          .selectFrom(member)
          .where(allEq(usernameParam, ageParam))
          .fetch();
}

private BooleanExpression usernameEq(String usernameParam) {
	return usernameParam != null ? member.username.eq(usernameParam) : null;
}

private BooleanExpression ageEq(Integer ageParam) {
	return ageParam != null ? member.age.eq(ageParam) : null;
}

// 메소드 조합
private BooleanExpression allEq(String usernameParam, Integer ageParam) {
	return usernameEq(usernameParam).and(ageEq(ageParam));
}

 

참고

김영한 님의 실전! Querydsl

김영한님의 자바 ORM 표준 JPA 프로그래밍

댓글