2 분 소요

인프런 실전! Querydsl 강의 내용 정리

  • 스프링 데이터의 Page, Pageable을 활용해 보자. 스프링 데이터 JPA - 페이징
  • 전체 카운트를 한번에 조회하는 단순한 방법
  • 데이터 내용과 전체 카운트를 별도로 조회하는 방법

먼저 간단한 페이지를 써 보자. MemberRepositoryCustom인터페이스에 메서드를 정의하자.

1
Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable);

구현 해 보자.

MemberRepositoryImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override  
public Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable) {  
    QueryResults<MemberTeamDto> results = queryFactory  
            .select(new QMemberTeamDto(  
                    member.id.as("memberId"),  
                    member.username,  
                    member.age,  
                    team.id.as("teamId"),  
                    team.name.as("teamName")  
            ))  
            .from(member)  
            .leftJoin(member.team, team)  
            .where(  
                    usernameEq(condition.getUsername()),  
                    teamNameEq(condition.getTeamName()),  
                    ageGoe(condition.getAgeGoe()),  
                    ageLoe(condition.getAgeLoe())  
            )  
            .offset(pageable.getOffset())  
            .limit(pageable.getPageSize())  
            .fetchResults();  
  
    List<MemberTeamDto> content = results.getResults();  
    long total = results.getTotal();  
  
    return new PageImpl<>(content, pageable, total);  
}

Querydsl 구문은 크게 다를 건 없고, 이전 검색 쿼리를 그대로 사용했다. 여기서 다른 건

1
2
.offset(pageable.getOffset())  
.limit(pageable.getPageSize())

파라미터로 받아온 pageable을 사용해서 페이징 처리를 한다는 것과 .fetchResults(); 로 페이징 객체를 반환 받는다는 것이다. 하지만 .fetchResults();는 현재

deprecated다… 이게 카운트 쿼리가 정확히 안나오는 경우도 있기 때문에 카운트 쿼리를 따로 날리는게 요즘엔 맞는 방법 인 것 같다.

아무튼 페이징 객체를 받아서

1
2
3
4
List<MemberTeamDto> content = results.getResults();  
long total = results.getTotal();  
  
return new PageImpl<>(content, pageable, total);

다음과 같이 넘겨 줬다.

이제 테스트를 해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Test  
public void searchPageSimpleTest() throws Exception {  
    //given  
    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", 30, teamB);  
    Member member4 = new Member("member4", 40, teamB);  
    em.persist(member1);  
    em.persist(member2);  
    em.persist(member3);  
    em.persist(member4);  
  
    MemberSearchCondition condition = new MemberSearchCondition();  
    PageRequest pageRequest = PageRequest.of(0, 3);  
  
    //when  
    Page<MemberTeamDto> result = memberRepository.searchPageSimple(condition, pageRequest);  
    //then  
    assertThat(result.getSize()).isEqualTo(3);  
    assertThat(result.getContent()).extracting("username").containsExactly("member1", "member2", "member3");  
}

테스트 자체도 기존 search 메서드 테스트 했을 때랑 비슷한데,

1
PageRequest pageRequest = PageRequest.of(0, 3);

페이징 객체를 만들어서 넘겨준다는 차이가 있다.

그래서 검증 해 보면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
select
        m1_0.member_id,
        m1_0.username,
        m1_0.age,
        t1_0.team_id,
        t1_0.name 
    from
        member m1_0 
    left join
        team t1_0 
            on t1_0.team_id=m1_0.team_id 
    offset
        ? rows 
    fetch
        first ? rows only

offsetfetch가 쿼리로 나가는 걸 볼 수 있다.

이제 복잡한 페이징 쿼리를 사용해 보자.

데이터 내용과 전체 카운트를 별도로 조회하는 방법

searchPageComplex()메서드 구현

1
Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable);

인터페이스에 다음과 같이 정의하고

구현 해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Override  
public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {  
    List<MemberTeamDto> contents = queryFactory  
            .select(new QMemberTeamDto(  
                    member.id.as("memberId"),  
                    member.username,  
                    member.age,  
                    team.id.as("teamId"),  
                    team.name.as("teamName")  
            ))  
            .from(member)  
            .leftJoin(member.team, team)  
            .where(  
                    usernameEq(condition.getUsername()),  
                    teamNameEq(condition.getTeamName()),  
                    ageGoe(condition.getAgeGoe()),  
                    ageLoe(condition.getAgeLoe())  
            )  
            .offset(pageable.getOffset())  
            .limit(pageable.getPageSize())  
            .fetch();  
  
    long total = queryFactory  
            .select(member)  
            .from(member)  
            .leftJoin(member.team, team)  
            .where(  
                    usernameEq(condition.getUsername()),  
                    teamNameEq(condition.getTeamName()),  
                    ageGoe(condition.getAgeGoe()),  
                    ageLoe(condition.getAgeLoe())  
            )  
            .fetchCount();  
  
    return new PageImpl<>(contents, pageable, total);  
}

사실 뭐 크게 다르진 않지만,

장점은 카운트 쿼리는 사실 실제 가져오는 쿼리보다 간단한 경우가 종종 있다.

지금 여기서도 솔직히 team에 대해 조인할 필요가 있는지 생각해 볼 수 있다.

하지만! fetchCount() 이것도 deprecated다 ㅋㅋ

1
2
3
4
5
6
7
8
9
10
11
Long total = queryFactory  
        .select(member.count())  
        .from(member)  
        .leftJoin(member.team, team)  
        .where(  
                usernameEq(condition.getUsername()),  
                teamNameEq(condition.getTeamName()),  
                ageGoe(condition.getAgeGoe()),  
                ageLoe(condition.getAgeLoe())  
        )  
        .fetchOne();

따라서 다음과 같이 작성 하는 것이 바람직 하다.

테스트 해보자.

1
2
//when  
Page<MemberTeamDto> result = memberRepository.searchPageComplex(condition, pageRequest);

이전 테스트랑 동일하게 세팅하고 메서드만 Complex로 바꿔줬다.

통과는 잘 됐고,

또 카운트 쿼리도 따로 나간 걸 볼 수 있다.

댓글남기기