페이징 처리하는 법을 최대한 간단하게 정리해보았다
먼저 상황을 서술하자면, 게시글이 DB에 쌓여있다. 그리고 사용자가 요청한 페이지에 필요한 만큼의 게시글들을 찾아서 응답해주려 한다.
참고 링크 https://sm-studymemo.tistory.com/141
클라이언트의 페이징 요청을 처리하는데 필요한 정보가 담김
요청에 의해 처리된 페이지 정보가 담김
알고가기
Spring Data JPA 리포지토리에
Pagable을 매개변수로 받아, Page를 리턴해주는 메서드가 이미 존재함(기본 CRUD 메서드처럼)
-> findAll(Pageable pageable);
public PostRepository extends JpaRepository<Post, Long>{
// 만약 유저로 찾고싶으면, 아래 메서드 추가
Page<Post> findByUser(User user, Pageable pageable); // 메서드명, 파라미터 순서 지키기
}
public Page<Post> findAll(Pageable pageable){
return postRepository.findAll(pageable);
}
@GetMapping("/post")
public ResponseEntity<Page> find(Pageable pageable){
Page<Post> postPage = postService.findAll(pageable);
//Page의 getContent()에 엔터티들이 들어있음. 모두 DTO로 변환해주기.
//Page는 불변객체이므로, 새 Page 객체를 생성하여 사용.
List<PostDTO> postDtos = postPage.getContent().stream()
.map(PostDTO::new) // Post를 PostDTO로 변환
.collect(Collectors.toList());
Page<PostDTO> postDtoPage = new PageImpl<>(postDtos, pageable, postPage.getTotalElements());
return ResponseEntity.ok(postDtoPage);
}
클라이언트가 Page를 처리하도록, Page를 스프링 웹 MVC가 JSON 및 메타데이터로 변환하여 응답해줌 (따라서 위 코드처럼 ResponseEntity에 Page 그대로 담아서 응답해도 됨)
ex) http://localhost:8080/post?page=0&size=5&sort=title,asc
public interface PostRepository extends JpaRepository<Post, Long> {
@Query(value = "select p from Post p join fetch p.user",
countQuery = "select count(p) from Post p")
Page<Post> findAllWithUser(Pageable pageable);
}
public interface PostRepository extends JpaRepository<Post, Long> {
@EntityGraph(attributePaths = {"user"}) //Post의 필드명
@Query("select p from Post p")
Page<Post> findAllWithUser(Pageable pageable);
}
OneToMany 관계에서 페치조인 사용시 문제가 발생함
페이지네이션은 SQL 결과 테이블의 row 단위로 처리함. 그런데 일대다 조인에서는 같은 엔터티가 중복된 row들이 나옴. 따라서 문제가 발생함.결과 테이블 ex) 유저 게시글 1 user1 post1 2 user1 post2 3 user1 post3그럼 JPQL의 distinct를 쓰면 안되나? 이유는 모르겠는데 안된다고 함(왜 안되는 지 나중에 질문해보기)
jpa.hibernate.default_batch_fetch_size=1000
# 컬렉션의 갯수가 n개라면, 원래는 1 + n 번 지연조회가 일어남. 그런데 이 n을 한 번에 1000개씩 묶어서 조회해줌
# 만약 컬렉션의 갯수가 2000개면 1 + 2 번 조회가 일어남
# SQL의 IN절을 사용함
# size의 최댓값은 1000임, 단 부하가능성이 있으므로 100~1000개 사이로 지정, 애매하면 500으로 사용
@BatchSize로 엔터티의 컬렉션 필드 또는, 엔터티 클래스 단위에 개별 지정하기
//클래스 단위로 사용시
@BatchSize(size = 500)
class User{
//필드 단위로 사용시
@BatchSize(size = 500)
@OneToMany(mappedBy = "user")
private List<Post> posts = new ArrayList<>();
}