Spring Data JPA

페이징과 정렬

유휴 2022. 12. 18. 01:10
 

실전! 스프링 데이터 JPA - 인프런 | 강의

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.

www.inflearn.com

 

Spring Data JPA - Reference Documentation

Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

목표

- 페이징 및 정렬에 사용되는 인터페이스와 클래스를 확인해보고 구현해본다.

 

페이징과 정렬

스프링 데이터는 페이징과 정렬기능을 간편하게 사용할 수 있는 기능을 제공해준다.

- 쿼리메서드의 '메서드이름으로 쿼리 생성' 기능을 사용하여 페이징 하는 방법

- org.springframework.data.domain 패키지의 인터페이스와 클래스를 직접 사용하여 페이징 하는 방법

 

쿼리메서드의 '메서드이름으로 쿼리 생성' 기능을 사용하여 페이징 하는 방법

First와 Top 키워드를 사용하여 페이징, Desc, Asc 키워드를 사용하여 정렬 할 수 있다.

Member findFirstByOrderByNameAsc();

Member findTopByOrderByAgeDesc();

Page<Member> queryFirst10ByName(String name, Pageable pageable);

Slice<Member> findTop3ByName(String name, Pageable pageable);

List<Member> findFirst10ByName(String name, Sort sort);

List<Member> findTop10ByUName(String name, Pageable pageable);

 

인터페이스와 클래스를 직접 사용하여 페이징 하는 방법

org.springframework.data.domain 패키지에 페이징 및 정렬을 위한 인터페이스 및  클래스가 정의되어 있다.

 

파라미터로사용되는 인터페이스 및  클래스

Sort :  정렬 기능을 기능을 정의해놓은 클래스

Pageable : 페이징 기능을 정의해놓은 인터페이스 (내부에 sort 클래스가 포함되어있다.)

PageRequest : Pageable 인터페이스를 구현한 추상클래스 AbstractPageRequest를 상속받아 구현된 클래스

 

반환타입으로 사용되는 인터페이스

Page : 추가 count 쿼리 결과를 포함하는 페이징 인터페이스 (Slice 인터페이스를 상속받는다)

Slice : 추가 count 쿼리 없이 다음 페이지 여부만 확인 가능한 페이징 인터페이스 (limit + 1 조회)

해당 인터페이스들의 구현체는 Spring Data Jpa에서 타입에맞게 반환해주며, PageImpl, SliceImpl 등이 있다.

Page와 Slice 인터페이스는 map 메서드를 지원하여 엔티티를 DTO로 편하게 변환할 수 있다.

 

Page 및 Slice 인터페이스

public interface Page<T> extends Slice<T> {
    int getTotalPages(); //전체 페이지 수
    long getTotalElements(); //전체 데이터 수
    <U> Page<U> map(Function<? super T, ? extends U> converter); //변환기
}
public interface Slice<T> extends Streamable<T> {
    int getNumber();
    int getSize();
    int getNumberOfElements();
    List<T> getContent();
    boolean hasContent();
    Sort getSort();
    boolean isFirst();
    boolean isLast();
    boolean hasNext();
    boolean hasPrevious();
    Pageable getPageable(); // 현재 페이지 객체
    Pageable nextPageable(); // 다음 페이지 객체
    Pageable previousPageable(); // 이전 페이지 객체
    <U> Slice<U> map(Function<? super T, ? extends U> converter); // 변환기
}

 

페이징 및 정렬 구현코드

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {

    Page<Member> findPagebyAge (int age, Pageable pageable);
    
    Slice<Member> findSlicebyAge (int age, Pageable pageable);
    
    List<Member> findListWithPageablebyAge (int age, Pageable pageable);
    
    List<Member> findListWithSortbyAge (int age, Sort sort);
    
    // @Query를 사용하면 countQuery를 분리하여 사용할 수 있다.(속도개선)
    @Query(value = "select m from Member m",
           countQuery = "select count(m.username) from Member m")
    Page<Member> findPageByAge(Pageable pageable);
}

@Service
public class MemberService {

    public Page<Member> findByAge{
   
        Sort sort = Sort.by(Sort.Direction.DESC, "username");
        // PageRequest.of(Page, Size, Sort);
        PageRequest pageRequest = PageRequest.of(0, 10, sort);
        Page<Member> page = memberRepository.findByAge(10, pageRequest);
        return page;
    }
    
    // 페이지를 유지하면서 엔티티를 Dto로 변환하기
    public Page<MemberDto> findDtoByAge{
   
        Sort sort = Sort.by(Sort.Direction.DESC, "username");
        // PageRequest.of(Page, Size, Sort);
        PageRequest pageRequest = PageRequest.of(0, 10, sort);
        Page<Member> page = memberRepository.findByAge(10, pageRequest);
        // page객체의 map 메서드를 통해 편하게 변환가능
        Page<MemberDto> dtoPage = page.map(m -> new MemberDto());
        return dtoPage;
    }
}