[Querydsl] Querydsl 동적으로 정렬하는 방법
게시글 조회 화면에서 검색타입, 정렬 방법에 따라 다른 결과를 반환해야 하는 기능을 구현하고 있었다.
단순한 검색 기능이면 스프링 데이터 JPA로 JPQL을 사용했겠지만, 이번 기능은 고려해야 할 부분이 많았기 때문에 Querydsl을 사용해서 구현해 보기로 했다.
where문 작성의 경우 BooleanExpression과 BooleanBuilder를 사용해서 간단하게 작성할 수 있었는데, 정렬 조건에 따른 작성을 어떻게 해야 하는지가 문제였다.
처음엔 service단에서 sort값에 따라 처리하도록 아래와 같이 작성했었다.
//PostService
public Page<Post> findPosts(int page, int size, PostSearchDto postSearchDto, String sort) {
PageRequest pageRequest = PageRequest.of(page, size);
if (sort != null && sort.equals("Likes")) {
return postRepository.getPostsSortByLikes(postSearchDto, pageRequest);
} else {
return postRepository.getPostsSortByNewest(postSearchDto, pageRequest);
}
}
//PostRepositoryImpl
//최신순
@Override
public Page<Post> getPostsSortByNewest(PostSearchDto postSearchDto, Pageable pageable) {
List<Post> results = queryFactory
.select(post)
.from(post)
.leftJoin(post.pet, pet)
.where(codeEq(postSearchDto.getCode()), typeEq(postSearchDto))
.orderBy(post.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
//...
}
//좋아요 순
@Override
public Page<Post> getPostsSortByLikes(PostSearchDto postSearchDto, Pageable pageable) {
List<Post> results = queryFactory
.select(post)
.from(post)
.leftJoin(post.pet, pet)
.where(codeEq(postSearchDto.getCode()), typeEq(postSearchDto))
.orderBy(post.likesCnt.desc())
.orderBy(post.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
//...
}
댓글 순까지 작성하면 orderBy를 제외한 다른 절들은 똑같은데 orderBy 때문에 3개의 메서드가 작성되는 게 별로였고 추후에 조건이 추가된다면 또 메서드를 만들어야 한다.
OrderSpecifier
Querydsl은 동적으로 정렬조건을 적용할 수 있게 OrderSpecifier를 제공한다.
@Override
public Page<Post> getPosts(PostSearchDto postSearchDto, Pageable pageable, String sort) {
List<Post> results = queryFactory
.select(post)
.from(post)
.leftJoin(post.pet, pet).fetchJoin()
.leftJoin(post.postComments, postComment)
.where(codeEq(postSearchDto.getCode()), typeEq(postSearchDto))
.orderBy(getOrderSpecifier(sort).stream().toArray(OrderSpecifier[]::new))
.groupBy(post.id)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
//...
}
private List<OrderSpecifier> getOrderSpecifier(String sort) {
List<OrderSpecifier> orders = new ArrayList<>();
if (StringUtils.hasText(sort)) {
Order direction = Order.DESC;
switch (sort) {
case "likes":
orders.add(new OrderSpecifier(direction, post.likesCnt));
orders.add(new OrderSpecifier(direction, post.id));
break;
case "comments":
orders.add(new OrderSpecifier(direction, postComment.id.count()));
orders.add(new OrderSpecifier(direction, post.id));
break;
case "newest":
orders.add(new OrderSpecifier(direction, post.id));
break;
default:
throw new BusinessLogicException(ExceptionCode.INVALID_SORT_TYPE);
}
}
return orders;
}
위와 같이 List에 정렬 조건별로 OrderSpecifier를 추가해서 반환하는 메서드를 만들어 사용하면 정렬 조건이 추후에 조건이 바뀌거나 추가되어도 쉽게 적용이 가능하다.
참고 블로그
Springboot JPA Querydsl 동적 정렬 OrderSpecifier
SQL 동적 정렬이란? 하나의 API에서 정렬 조건을 동적으로 변경해, 정렬 혹은 정렬 + 페이징을 진행하는 것을 의미합니다. 예시를 보며, 필요한 상황이 언제이며, 어떻게 해결하는지 알아가 보겠습
dingdingmin-back-end-developer.tistory.com
'Programming > Spring' 카테고리의 다른 글
[Spring] HttpServletRequest 요청마다 같은 주소값을 반환하는 이유 (0) | 2023.02.25 |
---|---|
[Spring] RequestContextHolder를 통해 HttpServletRequest, Response 가져오기 (0) | 2023.02.17 |
[Spring] 같은 타입의 빈 문제 해결 방법(@Primary, @Qualifier) (0) | 2023.02.15 |
[Spring] 스프링 컨테이너와 빈 (0) | 2023.02.13 |
[Spring] 예외처리 - ExceptionHandler, ControllerAdvice (0) | 2022.10.30 |