[Spring Data JPA] Page

김형진·2023년 5월 4일
0

순수 JPA에서는

//RepositoryImpl
    public List<Member> findByPageAndAge(int age, int offset, int limit){
        return entityManager.createQuery("select m from Member m where m.age = :age")
                .setParameter("age", age)
                .setFirstResult(offset)
                .setMaxResults(limit)
                .getResultList();
    }

와 같이 JPQL문을 이용해 페이징 쿼리가 가능하다. 단, 해당 JPQL로는 페이징된 contents의 결과만 볼 수 있으므로 페이징 계산을 위해 필요한 total값은

//RepositoryImpl
    public long getTotalCount(int age){
        return entityManager.createQuery("select count(m) from Member m where m.age = :age", Long.class)
                .setParameter("age", age)
                .getSingleResult();
    }

와 같이 따로 쿼리를 날려줘야 한다.

그런데, Spring Data에서 제공하는 Page 인터페이스를 사용하면 total쿼리를 따로 생성하지 않아도 된다.

spring data jpa의 repository interface에

//Repository Interface
 Page<Member> findByAge(int age, Pageable pageable);

와 같이 return값은 Page인터페이스, 파라미터로는 Pageable인터페이스를 명시하고

//client
PageRequest pageRequest = PageRequest.of(1,3, Sort.by(Sort.Direction.ASC, "username"));

Page<Member> result = memberRepository.findByAge(5, pageRequest);

와 같이 offset, limit, sorting 정보가 담긴 PageRequest객체를 파라미터로 넘겨주면 spring data에서는 spring data의 Page 인터페이스를 구현한 객체를 result로 넘겨주는데,
이 result객체에 현재 페이지, 총 페이지 수, 총 데이터 수 등 페이징에 필요한 모든 정보가 들어있다.
스프링 데이터가 내부적으로 total count 쿼리를 한 번 더 날리고 알아서 페이징을 계산해 result객체에 전부 담아준 것이다.

total count 쿼리를 따로 날릴 필요가 없을 뿐 아니라 페이징 계산도 알아서 해주기 때문에 아주 편한 기능이지만 주의해야 할 점은 spring이 내부적으로 날리는 total count 쿼리는 contents를 가져올 때의 쿼리와 똑같이 table join하므로
table join이 많은 경우 total count조회 시 성능이 크게 떨어진다는 점이다.

그래서 total count가 주요 테이블과 무조건 일치하는 경우에는(다른 테이블과의 조인이 굳이 필요 없는 경우)

//repository
@Query(value = "select m from Member m left join m.team", countQuery = "select count(m) from Member m")
Page<Member> findByAge(int age, Pageable pageable);

와 같이 직접 contents조회 쿼리와 countQuery를 직접 명시하여 total count 조회 시 테이블 조인을 하지 않게 하는 것이 좋다.

  • Page인터페이스는 Slice라는 인터페이스를 상속한다.
    Slice인터페이스는 totalCount나 총 페이지 수와 관련된 인터페이스를 제공하지 않는다. 대신 다음 페이지가 있는지 여부를 알게 해주는 인터페이스를 제공하고 있어 무한스크롤과 같이 페이지 번호가 필요 없는 경우에 사용된다고 한다.
    근데 어차피 Page인터페이스가 Slice를 직접 상속받아 기능을 확장하기 때문에
    무한스크롤이 될지 페이지네이션이 될지 굳이 알 필요가 없는 화면에 의존적이지 않은 API를 구현할 때에는 Page인터페이스를 사용하는 것이 좋을 것 같다.
profile
히히

0개의 댓글