순수 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 조회 시 테이블 조인을 하지 않게 하는 것이 좋다.