본문 바로가기
JPA/Querydsl

Querydsl 05 - 서브쿼리

by 킹차니 2022. 1. 18.

서브쿼리

'com.querydsl.jpa.JPAExpressions'사용

 

바로 코드를 보며 알아보자.

그전에 아래 테스트 코드들의 모든 각 테스트에 @BeforeEach가 적용된다.

    @BeforeEach
    public void before(){
        queryFactory = new JPAQueryFactory(em);

        Team teamA = new Team("teamA");
        Team teamB = new Team("teamB");
        em.persist(teamA);
        em.persist(teamB);

        Member member1 = new Member("member1", 10, teamA);
        Member member2 = new Member("member2", 20, teamA);
        Member member3 = new Member("member3", 10, teamB);
        Member member4 = new Member("member4", 20, teamB);
        Member member5 = new Member("member5", 55, teamB);
        em.persist(member5);
        em.persist(member1);
        em.persist(member2);
        em.persist(member3);
        em.persist(member4);
    }

 

 

1. 나이가 가장 많은 회원 조회하기

/*나이가 가장 많은 회원 조회*/
@Test
void subQuery(){
    // 서브쿼리에서 사용할 QMember를 하나 더 만들어줘야 한다.
    QMember memberSub = new QMember("memberSub");
    List<Member> result = queryFactory.selectFrom(member)
            .where(member.age.eq(
                    //서브쿼리
                    JPAExpressions.select(memberSub.age.max()).from(memberSub)
            ))
            .fetch();

    for (Member member : result) System.out.println("member = " + member);
}

출력결과:
member = Member(id=3, username=member5, age=55)

 

2. 나이가 평균 이상인 회원 조회하기

/*나이가 평균 이상인 회원 조회*/
@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();
    for (Member member : result) System.out.println("member = " + member);
}

출력결과:
member = Member(id=3, username=member5, age=55)

 

3. in절에 서브쿼리를 사용하여 나이가 10 이상인 회원들 조회하기

/* in subquery */
@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();

    for (Member member : result) System.out.println("member = " + member);
}

출력결과:
member = Member(id=3, username=member5, age=55)
member = Member(id=5, username=member2, age=20)
member = Member(id=7, username=member4, age=20)

 

4. select안에 서브쿼리 사용하기

void selectSubQuery(){
    QMember memberSub = new QMember("memberSub");

    List<Tuple> result = queryFactory.select(
            member.username,
            JPAExpressions.select(memberSub.age.avg()).from(memberSub)
        ).from(member).fetch();
    for (Tuple tuple : result) System.out.println("tuple = " + tuple);
}

출력결과:
tuple = [member5, 23.0]
tuple = [member1, 23.0]
tuple = [member2, 23.0]
tuple = [member3, 23.0]
tuple = [member4, 23.0]

 

from 절의 서브쿼리 한계

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

 

from 절의 서브쿼리 해결방안

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

 

 

 

김영한님의 인프런 강의와 PDF를 바탕으로 정리하였습니다.