JPA/Querydsl

Querydsl - 정렬과 페이징, 집합

chanhee01 2023. 8. 12. 23:28

정렬

/**
* 회원 정렬 순서
* 1. 회원 나이 내림차순(desc)
* 2. 회원 이름 올림차순(asc)
* 만약, 회원 이름이 없으면 마지막에 출력(nulls last)
*/
@Test
public void sort() {
    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))
            .orderBy(member.age.desc(), member.username.asc().nullsLast())
            // nullsLast는 해당 데이터가 null이면 마지막으로 정렬 (nullsFirst는 맨 앞으로)
            .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();
}

기본 데이터들의 나이는 적기 때문에 테스트를 위해 새로 persist한 데이터들이 조건에 의해서 먼저 나오게 될 것이다. 나이가 많은 순으로 정렬하고 이름 순으로 올림차순 했으니 member5가 첫 번째, member6가 두 번째이다. memberNull은 이름이 없다. 하지만 member.username.asc().nullsLast라는 문장에 의해 나이로 정렬로 한 뒤, 이름의 정렬을 할 때 맨 뒤로 가기때문에 나이가 같은 member5, member6 바로 아래에서 조회가 되는 것이다.

 

 

 

desc(), asc()로 기본 정렬

nullsLast(), nullsFirst()로 null 값 정렬

 

 

 

 

페이징

@Test
public void paging1() {
    List<Member> result = queryFactory
            .selectFrom(member)
            .orderBy(member.username.desc())
            .offset(0)
            .limit(2)
            .fetch();

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

@Test
public void paging2() { // 전체 조회수까지 필요할 때
    QueryResults<Member> result = queryFactory
            .selectFrom(member)
            .orderBy(member.username.desc())
            .offset(0)
            .limit(2)
            .fetchResults();

    assertThat(result.getTotal()).isEqualTo(4);
    assertThat(result.getLimit()).isEqualTo(2);
    assertThat(result.getOffset()).isEqualTo(0);
    assertThat(result.getResults().size()).isEqualTo(2);
}

전체 조회 개수가 필요하면 paging2()처럼 fetchResults()로 결과를 조회하면 된다.

하지만 성능 상 전체 조회 개수를 분리해야 한다면 paging1()처럼 fetch()를 사용하면 된다.

 

 

 

 

집합

@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);
}

count(), sum() 등의 집합을 사용하는데 JPQL과 방법이 매우 유사해서 별도의 설명 없이 이해할 수 있을 것이다.

 

 

 

@Test
public void group() throws Exception {
    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(35);
}

groupBy를 team.name으로 해오는 것이다. 반환 타입이 Tuple의 리스트로 반환되는 특징이 있다.

 

 

Querydsl을 공부하고 있는데 기본적으로 JPQL과 많이 겹치는데 JPQL을 간편하게 해주는 것이라 이해하기 어렵진 않지만 JPQL을 더 확실하게 공부하고 Querydsl도 실수 없이 사용할 수 있도록 익숙해져야 한다는 생각이 들었다.