-
Querydsl에서 스프링 데이터 JPA의 페이징 활용JPA/Querydsl 2023. 8. 13. 20:19
스프링 데이터 JPA에서 페이징을 쉽게 하는 방법을 공부했는데, Querydls과 연동해서 둘의 페이징 기능을 함께 사용하는 방법이다.
MemberRepositoryCustom 인터페이스에 선언을 해준다.
public interface MemberRepositoryCustom { Page<MemberTeamDto> searchPageSimple(MemberCond condition, Pageable pageable); }
Impl에 구현을 해준다.
@Override public Page<MemberTeamDto> searchPageSimple(MemberCond condition, Pageable pageable) { QueryResults<MemberTeamDto> results = queryFactory .select(new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName"))) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe())) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetchResults(); List<MemberTeamDto> content = results.getResults(); long total = results.getTotal(); return new PageImpl<>(content, pageable, total); // PageImpl<> : Page의 구현체 }
이전의 검색 코드와 동일하지만 변경된 것이 있다면 파라미터로 Pageable을 넘겨주었고, offset이랑 limit에 pageable의 요소들을 넣어줬다. 그리고 return으로 pageable의 content, pageable, total 순으로 반환해주었다.
@Test public void searchPageSimple() { Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); em.persist(teamA); em.persist(teamB); Member member1 = new Member("member1", 10, teamA); Member member2 = new Member("member2", 20, teamA); Member member3 = new Member("member3", 30, teamB); Member member4 = new Member("member4", 40, teamB); em.persist(member1); em.persist(member2); em.persist(member3); em.persist(member4); // 여기부터 MemberCond condition = new MemberCond(); PageRequest pageRequest = PageRequest.of(0, 3); Page<MemberTeamDto> result = memberRepository.searchPageSimple(condition, pageRequest); // 여기까지 assertThat(result.getSize()).isEqualTo(3); assertThat(result.getContent()).extracting("username").containsExactly("member1", "member2", "member3"); }
테스트 코드인데 주석처리 된 부분 사이에서 페이징이 처리된다. PageRequest()에 (page, size) 순서대로 넣어주고 그 pageRequest를 searchPageSimple의 두 번째 인자값으로 넣어주면 된다.
이 방식은 전체 목록을 count 해오기 때문에 불필요한 데이터를 조회하는 경우가 있다.
- count를 따로 가져오기
select 쿼리는 복잡한데 count 쿼리는 간단한 경우가 있을 수도 있는데, 이럴 때에는 count할 때에도 join하고 복잡해지기 때문에 최적화를 하지 못한다.
@Override public Page<MemberTeamDto> searchPageComplex(MemberCond condition, Pageable pageable) { List<MemberTeamDto> content = queryFactory .select(new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName"))) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe())) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch();// fetchResults()가 아니라 fetch()로 가져온다. count 빼고 content만 받아옴 long total = queryFactory .select(member) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe())) .fetchCount(); // 쿼리를 분리해서 count를 따로 쿼리로 select 해온다. return new PageImpl<>(content, pageable, total); // PageImpl<> : Page의 구현체 }
처음 쿼리에서 fetchResult()로 받아오지 않고 fetch()로만 받아오면 자동으로 content만 받아오게 된다.이후에 count를 하는 쿼리를 따로 만들어서 총 개수를 select해오게 하면 된다.
컨트롤러에서의 실행 결과
@GetMapping("v2/members") public Page<MemberTeamDto> searchMemberV2(MemberCond condition, Pageable pageable) { return memberRepository.searchPageSimple(condition, pageable); } @GetMapping("v3/members") public Page<MemberTeamDto> searchMemberV3(MemberCond condition, Pageable pageable) { return memberRepository.searchPageComplex(condition, pageable); }
v3/members로 접근했을 때에도 당연히 동일한 결과가 나온다.
'JPA > Querydsl' 카테고리의 다른 글
스프링 데이터 JPA와 Querydsl 동시 사용 (0) 2023.08.13 Querydls 동적 쿼리 활용 (0) 2023.08.13 Querydsl의 수정, 삭제 쿼리 (0) 2023.08.13 Querydsl의 동적 쿼리 (0) 2023.08.13 Dto로 프로젝션 결과 반환 (0) 2023.08.13