// SimpleJpaRepository public List<T> findAll(Sort sort) { return this.getQuery((Specification)null, (Sort)sort).getResultList(); } public Page<T> findAll(Pageable pageable) { return (Page)(isUnpaged(pageable) ? new PageImpl(this.findAll()) : this.findAll((Specification)null, pageable)); }
1. 정렬 파라미터
org.springframework.data.domain.Sort
: 정렬 가능
org.springframework.data.domain.Pageable
: 페이징 기능 (내부에 Sort 포함)
public interface Pageable {...}
- Pageable은 인터페이스 이다.
AbstractPageRequest
public abstract class AbstractPageRequest implements Pageable, Serializable { private static final longserialVersionUID= 1232825578694716871L; private final int page; private final int size; }
PageRequest (Pageable 구현체)
public class PageRequest extends AbstractPageRequest { private static final longserialVersionUID= -4541509938956089562L; private final Sort sort; protected PageRequest(int page, int size, Sort sort) { super(page, size); Assert.notNull(sort, "Sort must not be null!"); this.sort = sort; } public static PageRequest of(int page, int size) { returnof(page, size, Sort.unsorted()); } public static PageRequest of(int page, int size, Sort sort) { return new PageRequest(page, size, sort); } public static PageRequest of(int page, int size, Direction direction, String... properties) { returnof(page, size, Sort.by(direction, properties)); } public static PageRequest ofSize(int pageSize) { return PageRequest.of(0, pageSize); } ... }
- 세개의 필드 page, size, sort
- Pageable은 인터페이스이다. 따라서 실제 사용할 때는 해당 인터페이스를 구현한
org.springframework.data.domain.PageReqeust
객체를 사용한다.
2. 반환 타입
Page<User> findByLastname(String lastname, Pageable pageable); Slice<User> findByLastname(String lastname, Pageable pageable); List<User> findByLastname(String lastname, Pageable pageable);
org.springframework.data.domain.Page
- 추가 count 쿼리 결과를 포함하는 페이징
- ex) 총 게시물의 갯수
- 페이지는 0부터 시작한다.
public class PageImpl<T> extends Chunk<T> implements Page<T> { private static final longserialVersionUID= 867755909294344406L; private final long total; public PageImpl(List<T> content, Pageable pageable, long total) { super(content, pageable); this.total = (Long)pageable.toOptional().filter((it) -> { return !content.isEmpty(); }).filter((it) -> { return it.getOffset() + (long)it.getPageSize() > total; }).map((it) -> { return it.getOffset() + (long)content.size(); }).orElse(total); } }
- total이라는 필드가 있다. (count 쿼리를 통해서 값이 주입된다.)
org.springframework.data.domain.Slice
- 추가 count 쿼리 없이 다음 페이지만 확인 가능 (내부적으로 limit + 1 조회)
public class SliceImpl<T> extends Chunk<T> { private static final longserialVersionUID= 867755909294344406L; private final boolean hasNext; private final Pageable pageable; public SliceImpl(List<T> content, Pageable pageable, boolean hasNext) { super(content, pageable); this.hasNext = hasNext; this.pageable = pageable; } }
List
- 추가 count 쿼리 없이 결과만 반환
- Page를 통해서 사용 가능한 데이터의 총 개수 및 전체 페이지 수를 알 수 있다.
- 이는 카운트 쿼리를 실행함으로써 전체 페이지 수 및 전체 데이터의 개수를 알아낸다.
- 카운트 쿼리가 많은 비용이 드는 경우에는 Slice를 사용하면 된다. Slice는 다음 Slice가 존재하는지 여부만 알고 있다.
- 전체 데이터 셋의 크기가 큰 경우에는 Slice를 사용하는게 성능상 유리하다.
- 결과를 단순히 List로 받을 수 있다. 단순히 주어진 범위내의 엔티티를 검색하기 위한 쿼리만 실행된다.
Count 쿼리
select count(post_id) from posts
* count 쿼리 분리
@Query(value = "select m from Member m left join m.team t", countQuery = "select count(m) from Member m") Page<Member> findByAge(int age, Pageable pageable);
- 데이터는 left join, 카운트는 left join 안해된다.
- page는 count 쿼리까지 가져오므로 join이 많으면 카운트 쿼리 또한 join을 해서 가져온다 ⇒ 성능저하
- count 쿼리는 join이 필요 없다.
- countQuery를 통해서 쿼리를 분리시켜서 성능을 향상시킨다.
3. Paging Web support
Spring Data Jpa에서는
HandlerMethodArgumentResolver
인터페이스 구현체를 제공함으로써 컨트롤러 메소드의 파라미터로 Sort, Pageable 등을 사용할 수 있다.@GetMapping("/posts") public ApiResponse<Page<PostResponseDto>> getAllPosts (Pageable pageable) { Page<PostResponseDto> posts = postService.getAllPosts(pageable); return ApiResponse.ok(posts); }
- 파라미터로 Pageable or Sort 을 받을 수 있다.
PageableHandlerMethodArgumentResolver
는 컨트롤러 메소드에Pageable
타입의 파라미터가 존재하는 경우, 요청 파라미터를 토대로PageRequest
객체를 생성한다.
요청 파라미터
- ex) /member?page=0&size=3&sort=id,desc&sort=username,desc
- 쿼리 스트링으로 전달
- page : 현재 페이지, 0부터 시작한다.
- size : 한 페이지에 노출할 데이터 건수
- sort : 정렬 조건을 정의한다.
- ex) 정렬 속성, 정렬 속성 … (ASC | DESC), 정렬 방향을 변경하고 싶다으면 sort 파라미터 추가 (asc 생략 가능)
그 외의 기능
1. 기본값
@RequestMapping(value = "/members_page", method = RequestMethod.GET) public String list(@PageableDefault(size = 12, sort = “username”, direction = Sort.Direction.DESC) Pageable pageable) { ... }
- @PageableDefault 어노테이션을 사용해서 기본값을 설정해줄 수 있다.
2. 접두사
- 페이징 정보가 둘 이상이면 접두사로 구분한다.
- @Qualifier에 접두사명 추가 “{접두사명}_xxx”
- ex) /members?member_page=0&order_page=1
public String list( @Qualifier("member") Pageable memberPageable, @Qualifier("order") Pageable orderPageable, ...