Spring Data JPA 페이징 /정렬

Crow·2022년 6월 19일
0

Spring Boot

목록 보기
4/6

5. 페이징/정렬 처리

기존에 스프링과 Hibernate를 직접 이용하는 경우에는 try~catch와 같은 많은 양의 코드를 직접 작성하는데 비해 Spring Data JPA는 내부적으로
클래스를 생성하는 방식으로 동작하면서 코드가 없는 형식의 개발이 가능함

(현재도 로코드와 노코드로 개발된 플랫폼이 많음 시간이 갈수록 복잡한 개발방식 보단 쉽게 필요한 부분에만 신경을 쓰고 창의성과 문제 해결능력이 중요해지는거 같음)

페이징 처리와 정렬은 SQL을 공부하는데 반드시 필요한 부분임
특히 페이징은 DB의 종류마다 사용하는 기법이 다른 경우가 많아서
오라클은 인라인뷰, MySQL은 limit을 알아야만 했음

하지만 JPA는 내부적으로 이런 처리를 Dialect라는 존재를 이용해서 처리함

예를 들어 JDBC 정보가 MariaDB의 경우에는 자동으로 MariaDB를 위한 Dialect가 설정됨(프로젝트 로딩 시점에 출력되는 로그를 통해 확인 가능하며, application.properties 등을 이용해서 설정 가능)

JPA가 이처럼 실제 데이터베이스에서 사용하는 SQL의 처리를 자동하기 때문에
개발자들은 SQL이 아닌 API의 객체와 메서드를 사용하는 형태로 페이징 처리를 할 수 있게됨^오^b

Spring Data JPA에서 페이징 처리와 정렬은 특이하게도 findAll()이라는 메서드를 사용함

findAll()은 JpaRepository 인터페이스의 상위인 PagingAndSortRepository의 메서드로 파라미터로 전달되는 Pageable이라는 타입의 객체에 의해서 실행되는 쿼리를 결정하게됨

여기서 주의할 사항은 리턴 타입을 Page 타입으로 지정하는 경우에는 반드시 파라미터를 Pageable 타입을 이용해야 한다는 점임

2.5.1 Pageable 인터페이스

페이지 처리를 위한 가장 중요한 존재는
org.springframework.data.domain.Pageable 인터페이스임

해당 인터페이스는 페이지 처리에 필요한 정보를 전달하는 용도의 타입으로,
인터페이스이기 때문에 실제 객체를 생성할 때는 구현체인
org.springframework.data.domain.PageRequest 라는 클래스를 사용함

PageRequest 클래스는 특이하게도 protected로 선언되어 new를 이용할 수 없음
따라서 객체를 생성하기 위해서는 static한 of()를 이용해서 처리함

PageRequest 생성자를 보면 page,size Sort라는 정보를 이용해서 객체를 생성함

static 메서드인 of()의 경우 몇 가지의 형태가 존재하는데 이는 페이지 처리에 필요한 정렬 조건을 같이 지정하기 위해서임

  • of(int page, int size): 0부터 시작하는 페이지 번호와 개수(size), 정렬이 지정되지 않음
  • of(int page, int size, Sort.Direction direction, String....props): 0부터 시작하는 페이지 번호와 개수, 정렬의 방향과 정렬 기준 필드들
  • of(int page, int size, Sort sort): 페이지 번호와 개수, 정렬 관련 정보


2.5.2 페이징 처리

Spring Data JPA를 이용할 때 페이지 처리는 반드시 0부터 시작한다는 점을 기억해야함

MemoRepositoryTests 클래스의 일부

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
// 이하 생략

  @Test
  public void testPageDefault() {
      // 1페이지 10개
      Pageable pageable = PageRequest.of(0, 10);

      Page<Memo> result = memorepository.findAll(pageable);

      System.out.println(result);
  }

import 시에 org.springframework.data 관련 클래스들을 사용하도록 주의해야함

테스트 코드는 PageRequest.of()를 이용해서 1페이지의 데이터 10개를 가져오기 위한 파라미터로 0, 10을 전달함

주의 깊게 볼 부분 중 하나는 리턴 타입이 org.springframework.data.domain.Page 라는 점임
Page 타입이 흥미로운 이유는 목록만을 가져오는게 아닌 실제 페이지 처리에 필요한 전체 데이터의 개수를 가져오는 쿼리 역시 같이 처리하기 때문임

테스트 코드를 실행해 보면 다음과 같은 SQL이 실행됨

Hibernate: 
  select
      memo0_.mno as mno1_0_,
      memo0_.memo_text as memo_tex2_0_ 
  from
      tbl_memo memo0_ limit ?
Hibernate: 
  select
      count(memo0_.mno) as col_0_0_ 
  from
      tbl_memo memo0_

첫 번째 쿼리에선 MariaDB의 페이징 처리에 사용하는 limit 구문이 사용됨
두 번째 쿼리에서는 count()를 이용해서 전체 개수를 처리하는 것을 볼 수 있음

위와 같이 findAll()에 Pageable 타입의 파라미터를 전달하면 페이칭 처리된 쿼리들을 실행하고,
이 결과들을 이용해서 리턴 타입으로 지정된 Page<엔티티 타입>객체로 저장함

Page<엔티티 타입>은 쿼리 결과를 사용하기 위한 여러 메서드를 지원하는데
테스트 코드에 내용을 좀 더 추가해서 이를 알아보겠음

    @Test
  public void testPageDefault() {
      // 1페이지 10개
      Pageable pageable = PageRequest.of(0, 10);

      Page<Memo> result = memorepository.findAll(pageable);

      System.out.println(result);

      System.out.println("------------------------------------");

      System.out.println("Total Pages: " + result.getTotalPages());

      System.out.println("Total Count: " + result.getTotalElements());

      System.out.println("Page Number: " + result.getNumber());

      System.out.println("Page Size: " + result.getSize());

      System.out.println("has next page?: " + result.hasNext());

      System.out.println("first page?: " + result.isFirst());

  }

Page<엔티티 타입>을 이용해서 주로 사용하는 메서드들은 위와 같이 페이지 처리와 관련된 정보들임

실제 페이지의 데이터를 처리하는 것은 getContent()를 이용해서 List<엔티티 타입>으로 처리하거나 Stream<엔티티 타입>을 반환하는 get()을 이용할 수 있음

       System.out.println("------------------------------------");

      for (Memo memo : result.getContent()){
          System.out.println(memo);
      } 
profile
어제보다 개발 더 잘하기 / 많이 듣고 핵심만 정리해서 말하기 / 도망가지 말기 / 깃허브 위키 내용 가져오기

0개의 댓글