Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

SpringDataJpa 페이징, 정렬 본문

인프런/[인프런] Spring Data JPA

SpringDataJpa 페이징, 정렬

Seung__ 2021. 8. 29. 18:28

1. SpringDataJpa


스프링에서 페이징 기능을 표준화 시켰다 ㄷㄷㄷ

 

 

 

 

2. 페이징, 정렬 파라미터


  • org.springframework.data.domain.Sort : 정렬 기능
  • org.springframework.data.domain.Pageable : 페이징 기능 (내부에 Sort 포함)
  • data.jpa가 아니다 즉, DB에 관계없이 적용이 가능한다.

 

 

 

3. 특별한 반환 타입


  • org.springframework.data.domain.Page : 추가 count 쿼리 결과를 포함하는 페이징
    • TotalCount 가져오는 쿼리는 자동으로 날라감.
  • org.springframework.data.domain.Slice : 추가 count 쿼리 없이 다음 페이지만 확인 가능
    (내부적으로 limit + 1조회) --> limit이 10개면 11개 조회해둠 ㅋㅋ
  • List (자바 컬렉션): 추가 count 쿼리 없이 결과만 반환

 

 

 

4. 예제


  • 조건
    • 검색 조건: 나이가 10살
    • 정렬 조건: 이름으로 내림차순
    • 페이징 조건: 첫 번째 페이지, 페이지당 보여줄 데이터는 3건
  • Repository
    • Page를 반환 타입으로 받고
    • 입력 값으로 Pageable이 필요하다.
    • Pageable : Paging informain의 추상 인터페이스(구현체는 PageRequest)
 Page<Member> findByAge(int age, Pageable pageable);
  • Test
    • Paging처리를 위해 PageRequest에 paging정보를 담아서 건내줘야 한다.
    • page는 다양한 기능을 제공한다.
    • .getTotalElement(), .getNumber() 등등
 @Test
    public void paging(){
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 10));
        memberRepository.save(new Member("member3", 10));
        memberRepository.save(new Member("member4", 10));
        memberRepository.save(new Member("member5", 10));

        int age = 10;
        PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "userName"));

        //when
        Page<Member> page = memberRepository.findByAge(age, pageRequest);

        //then
        List<Member> content = page.getContent(); //data 꺼내기
        long totalCount = page.getTotalElements(); //totalCount

        Assertions.assertThat(content.size()).isEqualTo(3);
        Assertions.assertThat(page.getTotalElements()).isEqualTo(5);
        Assertions.assertThat(page.getNumber()).isEqualTo(0); //page 번호
        Assertions.assertThat(page.getTotalPages()).isEqualTo(2);
        Assertions.assertThat(page.isFirst()).isTrue();
        Assertions.assertThat(page.hasNext()).isTrue();
    }
  • 결과 : return type이 Page이기 때문에 count호출하는 쿼리가 나감.
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.user_name as user_nam3_0_ 
    from
        member member0_ 
    where
        member0_.age=? 
    order by
        member0_.user_name desc limit ?
        
        
       
    select
        count(member0_.member_id) as col_0_0_ 
    from
        member member0_ 
    where
        member0_.age=?

 

 

 

 

5. Slice


  • Slice : 요청할 때 limit + 1개 더, 그래서 전체 count가져오지 않음.
  • 반환 타입을 Page -> Slice로 바꿨더니 getTotalElements를 사용할 수 없다!!

 

  • Repository
   Slice<Member> findByAge(int age, Pageable pageable);
  • 참고로 Page는 Slice를 상속받는다. 반환 타입 설정에 주의!!

  • Test
    @Test
    public void paging(){
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 10));
        memberRepository.save(new Member("member3", 10));
        memberRepository.save(new Member("member4", 10));
        memberRepository.save(new Member("member5", 10));

        int age = 10;
        PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "userName"));

        //when
        //Page<Member> page = memberRepository.findByAge(age, pageRequest);
        Slice<Member> page = memberRepository.findByAge(age, pageRequest);

        //then
        List<Member> content = page.getContent(); //data 꺼내기
        //long totalCount = page.getTotalElements(); //totalCount

        Assertions.assertThat(content.size()).isEqualTo(3);
        //Assertions.assertThat(page.getTotalElements()).isEqualTo(5);
        Assertions.assertThat(page.getNumber()).isEqualTo(0); //page 번호
        //Assertions.assertThat(page.getTotalPages()).isEqualTo(2);
        Assertions.assertThat(page.isFirst()).isTrue();
        Assertions.assertThat(page.hasNext()).isTrue();
    }
  • slice는 limit + 1이라고 하는데 진짜로 그럴까?
  • 위의 코드를 보면 limit는 3으로 지정했는데... 로그를 보면 4로 들어가있다 ㅋㅋㅋㅋ
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.user_name as user_nam3_0_ 
    from
        member member0_ 
    where
        member0_.age=? 
    order by
        member0_.user_name desc limit ?
     
<p6spy>     
select 
    member0_.member_id as member_i1_0_, 
    member0_.age as age2_0_, 
    member0_.team_id as team_id4_0_,
    member0_.user_name as user_nam3_0_ 

from 
	member member0_ 
where 
	member0_.age=10 
order by 
	member0_.user_name desc limit 4;
  • Slice를 사용하면 미리 하나 더 불러와서 "더보기"누르면 하나 보여주도로 만듦 ㅋㅋㅋ

 

 

6. Query와 CountQuery 분리


  • totalCount는 결국 DB에 있는 전체 DATA수를 계산해야함. 즉 성능이 안나옴.
  • data가 많아질수록 답없네..
  • Query가 복잡하면 ContQuery도 복잡해짐...
  • member와 team을 left outer join한다고 해보자.
    @Query(value = "select m from Member m left join m.team t")
    Page<Member> findByAge(int age, Pageable pageable);
  • 그런데 Count쿼리는  join이 필요없다? 왜? 조인 하던 말던 member의 수에는 관계 없다. ㅋㅋㅋ
  • 근데 Count쿼리도 join이 날라간다.
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.user_name as user_nam3_0_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id 
    order by
        member0_.user_name desc limit ?

: 
    select
        count(member0_.member_id) as col_0_0_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id
  • 최적화다 필요하다. CountQuery를 따로 분리 할 피요가 있다.
  • CountQuery를 별도로 작성하며 단순하게 member의 수만 확인하도록 하자.
@Query(value = "select m from Member m left join m.team t",countQuery = "select count(m.userName) from Member m")
Page<Member> findByAge(int age, Pageable pageable);
  • 크 CountQuery가 심플해졌다.
   select
        count(member0_.user_name) as col_0_0_ 
    from
        member member0_

 

 

7. 참고


  • Page는 1부터 시작이 아니라 0부터 시작이다.
  • Paging에서 추가로 제공하는 정보 필요없이 단순이 data끊어서 가져오려면 List<>로 받아도 가능.
  • Page<Entity>는  API에 노출하면 안돼!!!
  • DTO로 변환해야함!
  • page.map을 활용하면 됨.
    • 현재 content를 주어진 function에 의해 다른 content로 바꾼 Page를 반환함.

 Page<MemberDto> toMap = page.map(member -> new MemberDto(member.getId(), member.getUserName(), null));

 

8. GitHub : 210829 SpringDataJpa, Paging


 

GitHub - bsh6463/SpringDataJpa

Contribute to bsh6463/SpringDataJpa development by creating an account on GitHub.

github.com

 

'인프런 > [인프런] Spring Data JPA' 카테고리의 다른 글

@EntityGraph  (0) 2021.08.29
Query Method, 벌크성 수정 쿼리  (0) 2021.08.29
JPA의 페이징과 정렬  (0) 2021.08.29
@Query, 반환 타입  (0) 2021.08.29
@Query, 파라미터 바인딩  (0) 2021.08.29
Comments