QueryDSL에서는 Pageable의 sort를 그대로 사용할 수 없어서 OrderSpecifier를 사용해 구현했다.
orderBy()의 파라미터 타입이 OrderSpecifier이기 때문이다.
OrderSpecifier의 생성자는 다음과 같이 Order와 Expression 타입의 객체를 받는다.
@Immutable
public class OrderSpecifier<T extends Comparable> implements Serializable {
private static final long serialVersionUID = 3427652988262514678L;
private final Order order;
private final Expression<T> target;
private final NullHandling nullHandling;
public OrderSpecifier(Order order, Expression<T> target, NullHandling nullhandling) {
this.order = order;
this.target = target;
this.nullHandling = nullhandling;
}
public OrderSpecifier(Order order, Expression<T> target) {
this(order, target, OrderSpecifier.NullHandling.Default);
}
// ...
}
- NullHandling: 특정 컬럼의 값이 null일 경우의 순서를 지정(기본값: Default)
- Default: 데이터베이스 기본 설정에 따라 null 순서가 결정됨
- NullsFirst: null일 경우 첫 번째로 이동
- NullsLast: null일 경우 마지막으로 이동
OrderSpecifier 생성자를 이용해 작성한 메서드는 다음과 같다.
class QueryDslUtil {
companion object {
fun getOrderSpecifier(qEntity: EntityPathBase<*>, sort: Sort): Array<OrderSpecifier<*>> {
val orders = arrayListOf<OrderSpecifier<*>>()
sort.forEach {
orders.add(
OrderSpecifier(
if (it.isAscending) Order.ASC else Order.DESC,
PathBuilder(qEntity.type, qEntity.metadata)
.get(it.property) as Expression<out Comparable<*>> // OrderSpecifier<T extends Comparable>
)
)
}
return orders.toTypedArray()
}
}
}
- 정렬할 Q타입의 클래스 객체(예: QTodo.todo)와 정렬에 대한 값을 가지고 있는 Pageable sort를 파라미터로 받는다.
- OrderSpecifier 객체를 저장할 리스트를 생성한다.
- Sort 객체에 포함된 모든 정렬 규칙에 대해 반복문을 수행하며 리스트에 값을 저장한다.
- 저장되는 값은 OrderSpecifier(정렬 기준, 정렬 기준 컬럼의 Path)이다.
- OrderSpecifier 객체가 담긴 리스트를 배열로 변환해 반환한다.
배열로 반환할 때의 주의점
- toArray: Array<Any?>타입의 배열을 반환
- orders.toArray() as Array<OrderSpecifier<*>>와 같이 타입 캐스팅을 하는 경우 ClassCastException 발생
- toTypedArray: 컴파일 시점에 요소의 타입 정보를 사용해 특정 타입의 배열을 생성
orderBy가 파라미터로 받는 값은 단일 값이거나 가변 인자이다.
그러므로 전개 연산자(*)를 사용해 orderBy에 값을 넣어준다.
override fun search(searchType: SearchType, keyword: String, pageable: Pageable): Page<Todo> {
// ...
val contents = queryFactory.selectFrom(todo)
.where(todo.isDeleted.isFalse.and(where))
.orderBy(*QueryDslUtil.getOrderSpecifier(todo, pageable.sort))
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.fetch()
return PageImpl(contents, pageable, totalCount)
}
서버를 실행해 다음과 같이 정렬 조건을 추가하여 요청을 보내면 정렬된 데이터를 확인할 수 있다.
서버 로그에서도 정렬 조건에 맞게 쿼리가 수행되는 것을 확인할 수 있다.
참고 자료
'JPA' 카테고리의 다른 글
Query DSL을 사용하기 위한 설정 (0) | 2024.07.03 |
---|