스프링데이터 + JPA/QueryDSL

33. 스프링 데이터 JPA 페이징 활용. QueryDSL 페이징 연동

sdafdq 2023. 12. 4. 07:12

먼저, 간단하게 QueryDSL에서 제공해 주는 기능.

 

@Override
public Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable) {
    QueryResults<MemberTeamDto> result = query.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())
            .fetchResults();
    List<MemberTeamDto> content = result.getResults();
    long total = result.getTotal();

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

이나, 

현재 deprecated 된 기능이다.

이유는 카운트 쿼리가 제대로 만들어지지 않을 수도 있기 때문.

JPQL에서는 본래 서브쿼리를 지원하지 않기 때문이라고 한다.

https://qwefdg3.tistory.com/924

그리고, count쿼리는 생각보다 자원이 많이 든다. 전체 데이터를 조회해야 하기 때문.

그런데 저렇게 join이나 where 더덕더덕 붙인거에, 카운트쿼리로 나가는 거다.

하다보면 카운트쿼리는 저런 조건들을 더덕더덕 붙이지 않아도 될 때가 있다. 

 

그래서, 카운트쿼리는 개발자가 직접 짜도록 유도하기 위해 이렇게 하는 것 같다.

fetchResults()는 fetchCount()도 같이 나가는데, 마찬가지인 이유로 fetchCount()도 deprecated 되었다.

 

그래서 이제 방법은, 사용자가 직접 콘텐츠와 카운트쿼리로 값을 가져온 다음, 페이지 객체로 만드는 것이다.

 

@Override
public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {
    List<MemberTeamDto> result = query.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= query.select(member.count()).from(member).leftJoin(member.team, team)
            .where(
                    usernameEq(condition.getUsername()),
                    teamNameEq(condition.getTeamName()),
                    ageGoe(condition.getAgeGoe()),
                    ageLoe(condition.getAgeLoe())
            );

    return PageableExecutionUtils.getPage(result, pageable, countQuery::fetchOne);
}

위에 콘텐츠 가져오는 것은 같다.

 

그런데, 보면 카운트쿼리를 값이 아닌 람다식으로,

       페이징 유틸리티

저 PageableExecuteionUtils.getPage() 라는 것에 주고 있다.

 

이유가 있다.

먼저 최적화를 위해서다.

먼저, 0번째 페이지를 가져왔는데 pageSize보다 콘텐츠의 사이즈가 작을 경우, 그 콘텐츠가 최대 갯수라는 소리이다.

그리고, 마찬가지로 마지막 페이지일 경우, 마지막 페이지의 콘텐츠 개수가 페이지 사이즈보다 작을 경우, 그것 또한 최대 사이즈를 구할 수 있다.

offset + 현재페이지 콘텐츠 사이즈

 

그리고, 카운트가 필요하기 전 까지 실행하지 않을 수 있다.

마치 엔티티의 지연로딩 처럼.

 

그리고,

countQuery::fetchOne 저게

() -> countQuery.fetchOne() 이거랑 같은데,

이상하게 람다는 경고를 주고 더블콜론은 경고를 주지 않는다.

질문해 봤다.

https://www.inflearn.com/questions/1091891

 

더블콜론은 여러 사용법이 있는데, 

 

1. 정적메소드 참조

클래스이름::정적메소드

 

2. 객체의 메소드 참조

객체::그객체의메소드

 

3. 들어올 객체의 메소드 참조

클래스명::메소드명

이거는 람다식으로 들어올 인자의 메소드를 쓸 수 있는거.

그러므로, 클래스가 맞아야 함.

예를 들어 String::toUpperCase

풀자면 (str -> str.toUpperCase())

결국엔 걔가 가지고 있는 메소드를 쓰는거임.

 

4. 생성자 참조

클래스명::new