BackEnd/JPA

[JPA] QueryDSL 기본문법 - 2

샤아이인 2022. 5. 13.

내가 공부한 것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼 겸 상세히 기록하고 얕은 부분들은 가볍게 포스팅하겠습니다.

 

5. 정렬

QueryDSL을 통해 정렬을 해보자!

  • desc() , asc() : 일반 정렬
  • nullsLast() , nullsFirst() : null 데이터 순서 부여
@Test
public void sort() {
    // given
    em.persist(new Member(null, 100));
    em.persist(new Member("member5", 100));
    em.persist(new Member("member6", 100));

    // when
    List<Member> members = queryFactory
            .selectFrom(member)
            .where(member.age.eq(100))
            .orderBy(member.age.desc(), member.username.asc().nullsLast())
            .fetch();

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

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

위 테스트 코드를 통해 알아보자. 

orderBy에 보면 desc()로 내림차순으로 정렬하고, nullsLast()와 같이 Null을 맨 뒤로 보내는 편리한 기능도 지원해준다.

 

나이는 모두 동일하게 100살이니 무시하고, 이름이 오름차순으로 되니 member5, member6, null 순으로 정렬된다.

 

실행되는 쿼리는 다음과 같다.

select
    member0_.member_id as member_i1_1_,
    member0_.age as age2_1_,
    member0_.team_id as team_id4_1_,
    member0_.username as username3_1_ 
from
    member member0_ 
where
    member0_.age=? 
order by
    member0_.age desc,
    member0_.username asc nulls last

 

6. 페이징

6-1) 일부분만 페이징 하는 경우

다음과 같이 테스트코드를 작성하였다.

@Test
public void paging_test() {
    // given
    List<Member> members = queryFactory
            .selectFrom(member)
            .orderBy(member.username.desc())
            .offset(1)
            .limit(2)
            .fetch();

    // then
    assertThat(members.size()).isEqualTo(2);
}

성공적으로 테스트가 통과되며, 생성되는 SQL은 다음과 같다.

select
    member0_.member_id as member_i1_1_,
    member0_.age as age2_1_,
    member0_.team_id as team_id4_1_,
    member0_.username as username3_1_ 
from
    member member0_ 
order by
    member0_.username desc limit 2 offset 1

 

6-2) 전체 조회 수가 필요한 경우

@Test
public void all_paging_test() {
    // given
    QueryResults<Member> queryResults = queryFactory
            .selectFrom(member)
            .orderBy(member.username.desc())
            .offset(1)
            .limit(2)
            .fetchResults();

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

다만 위 코드와 같이 fetchResults를 사용하는 방식은 count 쿼리가 원본 쿼리와 같이 모두 조인을 해버리기 때문에 성능이 안나올 수 있다.

count 쿼리에 조인이 필요없는 성능 최적화가 필요하다면, count 전용 쿼리를 별도로 작성하는것을 추천한다.

 

7. 집합

7-1) 집합 함수 코드

@Test
public void aggregation() {
    List<Tuple> result = queryFactory
            .select(member.count(),
                    member.age.sum(),
                    member.age.avg(),
                    member.age.max(),
                    member.age.min())
            .from(member)
            .fetch();

    Tuple tuple = result.get(0);
    assertThat(tuple.get(member.count())).isEqualTo(4);
    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);
}

반환값이 Tuple인것을 확인할 수 있다.

반환값이 한가지 타입이 아닌, 여러개의 type을 반환하는 경우 tuple을 사용한다.

 

하지만 실무에서는 Tuple을 사용하기 보다는 DTO로 직접 조회하는 방식을 더 자주 사용한다.

 

7-2) Group By 사용

@Test
@DisplayName("팀의 이름과 각 팀의 평균 연령 구하기")
public void group_test() {
    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");
    assertThat(teamA.get(member.age.avg())).isEqualTo(15);

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

groupBy에 조건을 걸려면 having() 또한 사용 가능하다.

 

예를들어 member의 age로 group by를 하되, 20살 이상만 그룹화 하려면?

.groupBy(member.age)
.having(member.age.gt(20))

'BackEnd > JPA' 카테고리의 다른 글

[JPA] QueryDSL 기본문법 - 4  (0) 2022.05.14
[JPA] QueryDSL 기본문법 - 3  (0) 2022.05.14
[JPA] QueryDSL 기본문법 - 1  (0) 2022.05.13
[JPA] 나머지 기능들  (0) 2022.05.07
[JPA] 스프링 데이터 JPA 분석  (0) 2022.05.06

댓글