쿼리 dsl을 적용하기 위해서는 스프링 데이터 JPA의 인터페이스에 사용자 정의 리포지토리를 상속해야한다.
public interface MemberRepo extends JpaRepository<Member, Long>, MemberCustom {
}
public interface MemberCustom {
List<MemTeamDTO> search(MemberSearchDTO condition);
List<MemTeamDTO> searchPageSimple(MemberSearchDTO condition, Pageable pageable);
List<MemTeamDTO> searchPageComplex(MemberSearchDTO condition, Pageable pageable);
}
public class MemberCustomImpl implements MemberCustom {
// 인터페이스의 각 메소드 구현
}
페이징 적용하기
MemberSearchDTO condition = new MemberSearchDTO();
PageRequest page = PageRequest.of(0, 2); // offset, limit
Page<MemTeamDTO> result = memberRepo.searchPageSimple(condition, page);
페이징 함수 호출시에 PageRequest를 인자로 넘겨서 호출한다.
@Override
public Page<MemTeamDTO> searchPageSimple(MemberSearchDTO condition, Pageable pageable) {
QueryResults<MemTeamDTO> results = qf
.select(new QMemTeamDTO(member.id.as("memberId"), member.username, member.age, team.id, 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<MemTeamDTO> content = results.getResults();
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
}
fetchResults 로 결과를 가져오게 되면 결과값 조회 쿼리 1개, count 쿼리 1개가 나간다.
count 쿼리 분리하기
분리하는 법은 간단하다. 위의 fetchResults를 fetch()와 fetchCount() 로 나누면 된다.
List<MemTeamDTO> content = qf
.select(new QMemTeamDTO(member.id.as("memberId"), member.username, member.age, team.id, team.name.as("teamName")))
.from(member)
.leftJoin(member.team, team)
.where( 조건 )
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
long total = qf
.select(new QMemTeamDTO(member.id.as("memberId"), member.username, member.age, team.id, team.name.as("teamName")))
.from(member)
.leftJoin(member.team, team) // 조인 없애도 됨
.where( // 조건)
.fetchCount();
분리했을 경우 이점
count 쿼리는 기본적으로 동일한 쿼리가 select 문에 count(*) 로만 바뀌어서 나가기 때문에 조회 쿼리가 복잡하면 성능이 안좋을 수 있다. 그래서 이렇게 분리했을 경우에는 count쿼리를 단순하게 만들 수 있다.
PageableExecutionUtils 적용
count 쿼리가 나가지 않아도 되는 경우는 두가지이다.
1. offset이 0일때 결과 갯수가 limit보다 작으면 그냥 결과 갯수가 총 count
2. 마지막 페이지면 offset + 결과 갯수 = count
이 둘을 처리해주는 것이 PageableExecutionUtils이다.
JPAQuery<MemTeamDTO> where = qf
.select(new QMemTeamDTO(member.id.as("memberId"), member.username, member.age, team.id, team.name.as("teamName")))
.from(member)
.leftJoin(member.team, team)
.where(//조건);
return PageableExecutionUtils.getPage(content, pageable, () -> where.fetchCount());
//좀 더 자바답게 수정하면
return PageableExecutionUtils.getPage(content, pageable, where::fetchCount);
count 쿼리를 분리했던 부분부터 리턴 값까지 이렇게 수정하면 된다. 그래서 만약 위 두가지 조건이 해당하지 않을 경우에만 getPage의 3번째 인자인 count 쿼리를 날리는 함수가 실행된다.
API에 적용하기
@GetMapping("/members")
public Page<MemTeamDTO> serchV3(MemberSearchDTO con, Pageable pageable){
return memberRepo.searchPageComplex(con, pageable);
}
테스트는 insomnia로 진행했다.
page와 size를 파라미터로 넘기면 Pageable이 생성되어 페이징이 잘 된 것을 확인할 수 있었다. 추가로
이렇게 size가 결과값의 갯수를 넘을 경우에는 count쿼리가 나가지 않는다.
관련글
https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard
'백엔드 > JPA' 카테고리의 다른 글
쿼리dsl - 조인과 서브쿼리 (0) | 2021.08.31 |
---|---|
스프링 데이터 jpa - 구현체 코드 구경, 쿼리 dsl (0) | 2021.08.23 |
스프링 데이터 JPA - 사용자 정의 클래스, Auditing, MVC 페이징 (0) | 2021.08.23 |
스프링 데이터 jpa 기본 기능정리 (0) | 2021.08.22 |
스프링 데이터 JPA 기초 (0) | 2021.08.22 |