SpringData JPA 페이징 심화

more·2023년 7월 27일
0

JPA 심화 2일차

목록 보기
3/5

페이징 레파지토리

  • JpaRepository 의존성
    • ListPagingAndSortingRepository
      • PagingAndSortingRepository : 여기에서 paging & sorting 기능을 제공

페이징 처리 프로세스

  1. PageRequest 를 사용하여 Pageable에 페이징 정보를 담아 객체화 한다.
  2. Pageable을 JpaRepository가 상속된 인터페이스의 메서드에 T(Entity)와 함꼐 파라미터로 전달한다.
  3. 2번의 메서드의 return 으로 Page<T>가 응답 된다.
  4. 응답된 Page<T>에 담겨진 Page 정보를 바탕으로 로직을 처리하면 된다.

페이징 요청/응답 클래스

Pageable

  • 요청 : org.springframework.data.domain.Pageable

    • 예시
    // 배열의 인덱스와 같이 0부터 시작하기 때문에 매개변수로 넘겨줄 때에 주의
    * 생성 방법
    PageRequest.of(int page, int size) : 0부터 시작하는 페이지 번호와 개수. 정렬이 지정되지 않음
    PageRequest.of(int page, int size, Sort sort) : 페이지 번호와 개수, 정렬 관련 정보
    PageRequest.of(int page int size, Sort sort, Direction direction, String ... props) : 0부터 시작하는 페이지 번호와 개수, 정렬의 방향과 정렬 기준 필드들
    
    * 제공하는 메서드
    pageable.getTotalPages() : 총 페이지 수
    pageable.getTotalElements() : 전체 개수
    pageable.getNumber() : 현재 페이지 번호
    pageable.getSize() : 페이지 당 데이터 개수
    pageable.hasnext() : 다음 페이지 존재 여부
    pageable.isFirst() : 시작페이지 여부
    pageable.getContent(), PageRequest.get() : 실제 컨텐츠를 가지고 오는 메서드. getContext는 List<Entity> 반환, get()은 Stream<Entity> 반환
  • 응답 : org.springframework.data.domain.Page
    - 페이징의 findAll() 의 기본적인 반환 메서드로 여러 반환 타입 중 하나이다.
    - 예시

    {
      "content": [
          {"id": 1, "username": "User 0", "address": "Korea", "age": 0},
          ...
          {"id": 5, "username": "User 4", "address": "Korea", "age": 4}
      ],
      "pageable": {
          "sort": {
              "sorted": false, // 정렬 상태
              "unsorted": true,
              "empty": true
          },
          "pageSize": 5, // 페이지 크기
          "pageNumber": 0, // 페이지 번호 (0번 부터 시작)
          "offset": 0, // 해당 페이지의 첫번째 요소의 전체 순번 (다음 페이지에서는 5)
          "paged": true,
          "unpaged": false
      },
      "totalPages": 20, // 페이지로 제공되는 총 페이지 수
      "totalElements": 100, // 모든 페이지에 존재하는 총 원소 수
      "last": false,  // 마지막 페이지 여부
      "number": 0,
      "sort": {
          "sorted": false,    // 정렬 사용 여부
          "unsorted": true,
          "empty": true
      },
      "size": 5,       // Contents 사이즈
      "numberOfElements": 5,  // Contents 의 원소 수
      "first": true,   // 첫페이지 여부
      "empty": false   // 공백 여부
    }
    	

페이지 반환 타입

Page<'T'> 타입

  • 게시판 형태의 페이징에서 사용된다.
  • 전체 요소 갯수도 함께 조회한다. (totalElements)
  • 응답은 위와 동일

Slice<'T'> 타입

  • 더보기 형태의 페이징에서 사용된다.
  • 전체 요소 갯수 대신 offset 필드로 조회할 수 있다.
    • 따라서 count 쿼리가 발생되지 않고 limit+1 조회를 한다. (offset 은 성능이 안좋아서 현업에서 안 씀)
  • Slice 응답 펼쳐 보기
    {
    	"content": [
    	  { "id": 13, "username": "User 12", "address": "Korea", "age": 12 },
    	  ...
    	  { "id": 16, "username": "User 15", "address": "Korea", "age": 15 }
    	],
    	"pageable": {
    	  "sort": { "sorted": false, "unsorted": true, "empty": true },
    	  "pageNumber": 3,
    	  "pageSize": 4,
    	  "offset": 12,
    	  "paged": true,
    	  "unpaged": false
    	},
    	"number": 3,
    	"numberOfElements": 4,
    	"first": false,
    	"last": false,
    	"size": 4,
    	"sort": { "sorted": false, "unsorted": true, "empty": true },
    	"empty": false
    }

List<'T'> 타입

  • 전체 목록보기 형태의 페이징에서 사용된다.
  • 기본 타입으로 count 조회가 발생하지 않는다.

=> T는 제네릭 타입이라는 뜻

정렬

컬럼 값으로 정렬하기

  • Sort 클래스를 사용

    • 예시
    
    Sort sort1 = Sort.by("name").descending();     // 내림차순
    Sort sort2 = Sort.by("password").ascending();  // 오름차순
    Sort sortAll = sort1.and(sort2);      // 2개이상 다중정렬도 가능하다 -> name 먼저하고 password 순으로
    Pageable pageable = PageRequest.of(0, 10, sortAll);  // pageable 생성시 추가

컬럼이 아닌값으로 정렬하기

  • @Query 사용시 Alias(쿼리에서 as 로 지정한 문구) 를 기준으로 정렬

    • 예시
    // 아래와 같이 AS user_password 로 Alias(AS) 를 걸어주면
    @Query("SELECT u, u.password AS user_password FROM user u WHERE u.username = ?1")
    List<User> findByUsername(String username, Sort sort);
    
    // 이렇게 해당 user_password 를 기준으로 정렬할 수 있다.
    List<User> users = findByUsername("user", Sort.by("user_password"));

SQL 함수를 사용해서 정렬하기

  • JpaSort 를 사용해서 쿼리 함수를 기준으로 정렬

    • 예시
    // 아래와 같이 일반적인 쿼리에서
    @Query("SELECT u FROM user u WHERE u.username = ?1") // 이건 없어도됨
    List<User> findByUsername(String username, Sort sort);
    
    // 이렇게 쿼리함수 LENGTH() 조건을 걸어서 password 문자길이 기준으로 정렬할 수 있다.
    List<User> users = findByUsername("user", JpaSort.unsafe("LENGTH(password)"));

페이징 & 정렬 실무 팁

1. List<'T'>가 필요하면 응답을 Page<'T'>로 받지말고 List<'T'> 로 받아라!

  • 전체 count 쿼리가 추가로 발생하는 Page<'T'> 보다는 List<'T'>가 대용량 처리할때 더 안정적이고 빠르다!

2. Pageable 과 실제 페이지사이의 -1 문제 해결하기

  • JPA 페이지는 0부터인데 화면은 1부터시작하는 문제
    • -1 처리를 중복으로 해줘야하는 이슈
  • PageDTO 를 만들어서 toPageable() 메소드를 사용해보자

3. Pageable 을 GET API의 요청필드로 받아오기

  • Pageable 을 API 요청필드에서 바로 받아올 수 있다.
    • 예시
    @GetMapping("/users")
    public Page<User> getAllUsers(Pageable pageable) {
        return userRepository.findAll(pageable);
    }
    => 근데 해당 방법은 잘 안씀 (url path가 계속 길어져서)
profile
조금 더

0개의 댓글