2 분 소요


스프링 데이터 페이징 활용1

Querydsl 페이징 연동

  • 스프링 데이터의 Page, Pageable 활용
  • 데이터 내용과 전체 카운트를 별도로 조회하는 방법

fetchResults(), fetchCount()

사용자 정의 인터페이스

public interface MemberRepositoryCustom {

    List<MemberTeamDto> search(MemberSearchCondition condition);

    Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable); // 구현 코드 생략..
    Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable);
}

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

/*
    * 복잡한 페이징
    * 데이터 조회 쿼리와 전체 카운트 쿼리를 분리
*/
public class MemberRepositoryImpl implements MemberRepositoryCustom {

    ...

    @Override
    public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {

        List<MemberTeamDto> content = queryFactory
                .select(new QMemberTeamDto(
                        member.id,
                        member.username,
                        member.age,
                        team.id,
                        team.name))
                .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
                .selectFrom(member)
                .leftJoin(member.team, team)
                .where(
                    usernameEq(condition.getUsername()),
                    teamNameEq(condition.getTeamName()),
                    ageGoe(condition.getAgeGoe()),
                    ageLoe(condition.getAgeLoe()))
                .fetchCount();

        return new PageImpl<>(content, pageable, total);
    }
}


스프링 데이터 페이징 활용2

CountQuery 최적화

        JPAQuery<Member> countQuery = queryFactory
                .selectFrom(member)
                .leftJoin(member.team, team)
                .where(
                    usernameEq(condition.getUsername()),
                    teamNameEq(condition.getTeamName()),
                    ageGoe(condition.getAgeGoe()),
                    ageLoe(condition.getAgeLoe()));

        // return new PageImpl<>(content, pageable, total);
        return PageableExcutionUtils.getPage(content, pageable, countQuery::fetchCount);
  • 스프링 데이터 라이브러리가 제공
  • count 쿼리가 생략 가능한 경우 생략해서 처리
    • 페이지 시작이면서 컨텐츠 사이즈가 페이지 사이즈보다 작을 때
    • 마지막 페이지일 때(offset + 컨텐츠 사이즈를 더해서 전체 사이즈 구함)


스프링 데이터 페이징 활용3

실제 컨트롤러

@RestController
@RequiredArgsConstructor
public class MemberController {

    private final MemberRepository memberRepository;

    ...
    @GetMapping("/v3/members")
    public Page<MemberTeamDto> searchMemberV3(MemberSearchCondition condition, Pageable pageable) {
        return memberRepository.searchPageComplex(condition, pageable);
    }
}
  • http://localhost:8080/v3/members?page=0&size=5


Querydsl 5.0 지원

스프링 부트 2.6부터는 Querydsl 5.0을 사용한다.
스프링 부트 2.6 이상 사용시 다음과 같은 부분을 확인해야 한다.

  1. build.gradle 설정 변경
  2. pageableExecutionUtils Deprecated(향후 미지원) 패키지 변경
  3. Querydsl fetchResults(), fetchCount() Deprecated(향후 미지원)

pageableExecutionUtils

pageableExecutionUtils 클래스 사용 패키지 변경
기능이 Deprecated된 것은 아니고, 사용 패키지 위치가 변경되었다. 기존 위치를 신규 위치로 변경하면 문제 없이 사용할 수 있다.

  • 기존 : org.springframework.data.repository.support.PageableExecutionUtils
  • 신규 : org.springframework.data.support.PageableExecutionUtils

Querydsl [fetchResults(), fetchCount()]

[참고]
Querydsl의 fetchResult(), fetchCount()는 개발자가 작성한 select 쿼리를 기반으로 count용 쿼리를 내부에서 만들어서 실행한다.
그런데 이 기능은 select 구문을 단순히 count 처리하는 용도로 바꾸는 정도이기 때문에, 단순한 쿼리에서는 잘 동작하지만, 복잡한 쿼리에서는 제대로 동작하지 않는다.
Querydsl은 향후 fetchResult(), fetchCount()를 지원하지 않기로 결정했다.

따라서, count 쿼리가 필요하면 다음과 같이 별도로 작성해야 한다.

count 쿼리 예제

@Test
void count() {
    Long totalCount = queryFactory
            // .select(Wildcard.count) // select count(*)
            .select(member.count()) // select count(member.id)
            .from(member)
            .fetchOne();
}
  • count(*)을 사용하고 싶다면 예제의 주석처럼 Wildcard.count를 사용하면 된다.
  • member.count()를 사용하면 count(member.id)로 처리된다.

위의 MemberRepositoryImpl.searchPageComplex() 예제처럼 select 쿼리와는 별도로 count 쿼리를 작성하고 fetch()를 사용해야 한다.

수정된 searchPageComplex 예제

public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {
    
    List<MemberTeamDto> content = queryFactory
            .select(new QMemberTeamDto(
                    member.id,
                    member.username,
                    member.age,
                    team.id,
                    team.name))
            .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();

    JPAQuery<Long> countQuery = queryFactory
            .select(member.count())
            .from(member)
            .leftJoin(member.team, team)
            .where(
                usernameEq(condition.getUsername()),
                teamNameEq(condition.getTeamName()),
                ageGoe(condition.getAgeGoe()),
                ageLoe(condition.getAgeLoe()));

    return PageableExcutionUtils.getPage(content, pageable, countQuery::fetchOne);
}


<출처 : 인프런 - 실전! Querydsl(김영한)>

댓글남기기