2 분 소요


도메인 모델과 테스트 기본 코드는 전 포스트 내용을 이어간다.
예제 도메인 모델


목차

  • 서브 쿼리
    • eq
    • goe
    • 여러 건 처리(in 사용)
    • select절에 subQuery
    • static import
  • Case 문
  • 상수, 문자 더하기


서브 쿼리

com.querydsl.jpa.JPAExpressions 사용

eq 사용

@Test
void subQuery() {
    QMember memberSub = new QMember("memberSub");

    List<Member> result = queryFactory
            .selectFrom(member)
            .where(member.age.eq(
                    JPAExpressions
                            .select(memberSub.age.max())
                            .from(memberSub)
            ))
            .fetch();

    assertThat(result)
            .extracting("age")
            .containsExactly(40);
}

goe 사용

@Test
void subQueryGoe() {
    QMember memberSub = new QMember("memberSub");

    List<Member> result = queryFactory
            .selectFrom(member)
            .where(member.age.goe(
                    JPAExpressions
                            .select(memberSub.age.avg())
                            .from(memberSub)
            ))
            .fetch();


    assertThat(result)
            .extracting("age")
            .containsExactly(30,40);
}

여러 건 처리(in 사용)

@Test
void subQueryIn() {
    QMember memberSub = new QMember("memberSub");

    List<Member> result = queryFactory
            .selectFrom(member)
            .where(member.age.in(
                    JPAExpressions
                            .select(memberSub.age)
                            .from(memberSub)
                            .where(memberSub.age.gt(10))
            ))
            .fetch();

    assertThat(result)
            .extracting("age")
            .containsExactly(20,30,40);
}

select 절에 subquery

@Test
void selectSubQuery() {

    QMember memberSub = new QMember("memberSub");

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

    for (Tuple tuple : result) {
        System.out.println("username = " + tuple.get(member.username));
        System.out.println("age = " + 
                tuple.get(JPAExpressions.select(memberSub.age.avg())
                    .from(memberSub)));
        }
}

결과

username = member1
age = 25.0
username = member2
age = 25.0
username = member3
age = 25.0
username = member4
age = 25.0

static import 활용

import static com.querydsl.jpa.JPAExpressions.select;

List<Member> result = queryFactory
        .selectFrom(member)
        .where(member.age.eq(select(memberSub.age.max())
                .from(memberSub)
        ))
        .fetch();

from 절의 서브쿼리 한계
JPA JPQL 서브쿼리의 한계점으로 from 절의 서브쿼리는 지원하지 않는다.(Querydsl도 마찬가지)
하이버네이트 구현체를 사용하면 select절의 서브쿼리는 지원한다.(Querydsl도 마찬가지)

from 절의 서브쿼리 해결방안

  1. 서브쿼리를 join으로 변경한다.(불가능한 상황도 있다.)
  2. 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
  3. nativeSQL을 사용한다.


Case 문

select, where, orderBy에서 사용 가능

단순한 조건

@Test
void basicCase() {
    List<String> result = queryFactory
            .select(member.age
                    .when(10).then("열살")
                    .when(20).then("스무살")
                    .otherwise("기타"))
            .from(member)
            .fetch();
}

복잡한 조건

@Test
void complexCase() {
    List<String> result = queryFactory
            .select(new CaseBuilder()
                    .when(member.age.between(0, 20)).then("0~20살")
                    .when(member.age.between(21, 30)).then("21~30살")
                    .otherwise("기타"))
            .from(member)
            .fetch();
}

orderBy에서 Case문 사용

  • 다음과 같은 임의의 순서로 회원을 출력하고 싶다면?
    1. 0 ~ 30살이 아닌 회원을 가장 먼저 출력
    2. 0 ~ 20살 회원 출력
    3. 21 ~ 30살 회원 출력
@Test
void orderByCase() {
    NumberExpression<Integer> rankPath = new CaseBuilder()
            .when(member.age.between(0,20)).then(2)
            .when(member.age.between(21,30)).then(1)
            .otherwise(3);

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

Querydsl은 자바 코드로 작성하기 때문에 rankPath처럼 복잡한 조건을 변수로 선언해서 select절, orderBy절에서 함께 사용할 수 있다.


상수, 문자 더하기

상수

상수가 필요하면 Expressions.constant(xxx) 사용

@Test
void constant() {
    Tuple result = queryFactory
            .select(member.username, Expressions.constant("A"))
            .from(member)
            .fetchFirst();
}

[참고] : 위와 같이 최적화가 가능하면 SQL에 constant값을 넘기지 않는다. 상수를 더하는 것 처럼 최적화가 어려우면 SQL에 constant값을 넘긴다.

문자 더하기

@Test
void concat() {
    String result = queryFactory
            .select(member.username.concat("_").concat(member.age.stringValue()))
            .from(member)
            .where(member.username.eq("member1"))
            .fetchOne();
}
  • 결과 : member1_10

[참고] : 문자가 아닌 다른 타입들은 stringValue()로 문자로 변환할 수 있다. 이 방법은 ENUM을 처리할 때도 자주 사용한다.


<출처 : 인프런 - 실전! Querydsl(김영한)>

댓글남기기