
프로젝트를 진행하던 중 동적 쿼리를 편하게 작성하기 위해 Querydsl을 도입했습니다.
기존 Spring Data의 Pageable객체를 Querydsl에 도입해 보려 합니다.
queryFactory
.selectFrom(recruitPost)
.orderBy(recruitPost.createdDate.desc()) // 정적 정렬 조건
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
가장 간단하게 Pageable의 정보를 활용한다면 위와 같이 작성할 수 있습니다.
하지만 아직 orderBy()에 Pageable의 Sort정보는 사용하지 않고 있습니다.

위와 같이 orderBy()는 하나 또는 배열(가변인자)인 OrderSpecifier 인자를 받고 있습니다.
Pageable을 받아 OrderSpecifier를 반환하는 메소드를 작성하면 될 것 같습니다.
Pageable의 Sort는 stream()으로 저장되어 있는 정렬 정보(Order)들을 가져올 수 있습니다.
각 Order에 담겨있는 정보로 OrderSpecifier를 생성해 주는 로직을 작성하면 될 것 같습니다.
pageable.getSort().stream().map(order -> {
/*
Sort.Order -> OrderSpecifier
정렬 방향: getDirection(), isAscending(), isDescending()
정렬에 사용할 속성: getProperty()
*/
})
Querydsl에서 어떤 엔티티의 속성에 대한 경로를 동적으로 생성해 줄 필요가 있습니다.
엔티티는 Querydsl의 QClass를 사용하지만 어떤 속성을 사용할지에 대한 정보는 Pageable객체에 있기 때문입니다.
이를 테면 QMember의 id를 기준으로 정렬할 때 QMember는 Querydsl에 id는 Pageable에 의존하고 있기 때문입니다. 저는 QMember.id에 대한 경로가 필요하기 때문에 경로를 생성해줄 메소드가 필요합니다.
다행히 Querydsl에서 PathBuilder라는 유연하게 경로 생성을 도와주는 객체가 있습니다.
public static <T> OrderSpecifier<?>[] getSort(Pageable pageable, EntityPathBase<T> qClass) {
PathBuilder<T> pathBuilder = new PathBuilder<>(qClass.getType(), qClass.getMetadata());
}
QClass의 getType()과 getMetadata()로 PathBuilder를 생성해 줍니다.
/* QuerydslUtils.java */
public abstract class QuerydslUtils {
public static <T> OrderSpecifier<?>[] getSort(Pageable pageable, EntityPathBase<T> qClass) {
PathBuilder<T> pathBuilder = new PathBuilder<>(qClass.getType(), qClass.getMetadata());
return pageable.getSort().stream().map(order -> new OrderSpecifier<>(
order.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.getString(order.getProperty())
)).toArray(OrderSpecifier[]::new);
}
}
Pageable의 Order를 사용해 Querydsl의 Order를 생성자 매개변수로 넘겨 줍니다.
order.getProperty()로 정렬에 사용할 속성을 가져와 동적으로 경로를 생성해 매개변수로 넘겨 줍니다.