[Querydsl] 11. 정렬, 페이징, 집합함수

민정·2023년 1월 9일

QueryDSL

목록 보기
11/18
post-thumbnail

✨ 정렬

✅ 요약

  • 일반정렬
    desc(), asc()

  • null 데이터 순서 부여
    nullsLast(), nullsFirst()

✅ 이러한 정렬 조건이 있을 때, querydsl을 사용해보자

  • 회원 정렬 순서
  1. 회원 나이 내림차순(desc)
  2. 회원 이름 올림차순(asc)
  3. 단, 2에서 회원 이름이 없으면 마지막에 출력(nulls last)
 /**
     * 정렬
     * 회원 정렬 순서
     * 1. 회원 나이 내림차순(desc)
     * 2. 회원 이름 올림차순(asc)
     * 단, 2에서 회원 이름이 없으면 마지막에 출력(nulls last)
     */
    @Test
    public void sort() {
        // 기존 4명의 회원에 3명의 회원 추가
        em.persist(new Member(null, 100));
        em.persist(new Member("member5", 100));
        em.persist(new Member("member6", 100));

        List<Member> result = queryFactory
                .selectFrom(member)
                .where(member.age.eq(100)) // 회원 중 나이가 100인 회원 찾기
                .orderBy(member.age.desc(), member.username.asc().nullsLast()) // 정렬
                .fetch();

        Member member5 = result.get(0);
        Member member6 = result.get(1);
        Member memberNull = result.get(2);

        assertThat(member5.getUsername()).isEqualTo("member5");
        assertThat(member6.getUsername()).isEqualTo("member6");
        assertThat(memberNull.getUsername()).isNull();
    }

✅ 핵심 코드

.orderBy(member.age.desc(), member.username.asc().nullsLast())



✨ 페이징

1. count 쿼리 X

fetch() 사용!

/**
     * 페이징
     */
    @Test
    public void paging1() {
        List<Member> result = queryFactory
                .selectFrom(member)
                .orderBy(member.username.desc())
                .offset(1) // 앞에 몇개 스킵할건지, 0부터 시작
                .limit(2) // 몇개의 데이터 가져올건지
                .fetch();

        assertThat(result.size()).isEqualTo(2);
    }

2. count 쿼리 O

fetchResults() 사용!

 /**
     * 페이징 - 전체 조회 수 필요
     */
    @Test
    public void paging2() {
        // fetchResults : 쿼리 2번(count query, contents가져오는 query)
        QueryResults<Member> queryResults = queryFactory
                .selectFrom(member)
                .orderBy(member.username.desc())
                .offset(1) // 앞에 몇개 스킵할건지, 0부터 시작
                .limit(2) // 몇개의 데이터 가져올건지, 최대 2건 조회
                .fetchResults();

        assertThat(queryResults.getTotal()).isEqualTo(4);
        assertThat(queryResults.getLimit()).isEqualTo(2);
        assertThat(queryResults.getOffset()).isEqualTo(1);
        assertThat(queryResults.getResults().size()).isEqualTo(2); // 컨텐츠
    }

getTotal, getLimit, getOffset, getResults 등의 함수 사용이 가능!

✅ 참고

실무에서는 페이징 쿼리 작성시, 데이터 조회하는 쿼리는 여러 테이블 조인해야하지만, count 쿼리는 조인이 필요없는 경우도 있다.

이런 경우 자동화된 count 쿼리는 원본 쿼리와 같이 모두 조인해버려서 성능이 안나올 수 있다.

따라서 count 전용 쿼리를 별도로 작성해 성능 최적화를 한다.



✨ 집합

✅ 집합 함수 제공

count, sum, avg, max, min

    /**
     * 집합
     * JPQL이 제공하는 집합함수 모두 제공
     *
     *  == JPQL ==
     *  select
     *  COUNT(m), //회원수
     *  SUM(m.age), //나이 합
     *  AVG(m.age), //평균 나이
     *  MAX(m.age), //최대 나이
     *  MIN(m.age) //최소 나이
     *  from Member m
     */
    @Test
    public void aggregation() {
        List<Tuple> result = queryFactory // 결과 타입 : querydsl이 제공하는 Tuple
                .select(
                        member.count(),
                        member.age.sum(),
                        member.age.avg(),
                        member.age.max(),
                        member.age.min()
                )
                .from(member)
                .fetch();

        Tuple tuple = result.get(0); // tuple 리스트에서 tuple 꺼내오고
        assertThat(tuple.get(member.count())).isEqualTo(4); // tuple.get(위에서 select 다음에 썼던 거 똑같이 작성)
        assertThat(tuple.get(member.age.sum())).isEqualTo(100);
        assertThat(tuple.get(member.age.avg())).isEqualTo(25);
        assertThat(tuple.get(member.age.max())).isEqualTo(40);
        assertThat(tuple.get(member.age.min())).isEqualTo(10);
    }

✅ group by

팀의 이름과 각 팀의 평균 연령을 구해라
groupBy(team.name)

/**
     * group by
     *
     * 팀의 이름과 각 팀의 평균 연령을 구해라
     */
    @Test
    public void group() {
        List<Tuple> result = queryFactory
                .select(team.name, member.age.avg())
                .from(member)
                .join(member.team, team)
                .groupBy(team.name)
                .fetch();

        Tuple teamA = result.get(0);
        Tuple teamB = result.get(1);

        assertThat(teamA.get(team.name)).isEqualTo("teamA"); // tuple.get(select절에 썼던 거)
        assertThat(teamA.get(member.age.avg())).isEqualTo(15);

        assertThat(teamB.get(team.name)).isEqualTo("teamB");
        assertThat(teamB.get(member.age.avg())).isEqualTo(35);
    }

✅ having

그룹화된 결과 제한

.groupBy(item.price)
.having(item.price.gt(1000))


✨ Tuple

package com.querydsl.core이 제공하는 Tuple의 get 함수이다.

  • 방법 1) index, Class를 통해서 get
  • 방법 2) Q타입의 속성을 사용해서 expr 작성



출처

김영한 강사님 - 인프런 실전! Querydsl

JPA - 객체지향 쿼리 언어 - QueryDSL 프로젝션

0개의 댓글