
QueryDSL을 사용해서 정렬을 수행하려면 orderBy()에 OrderSpecifier를 전달해주어야 한다. 다음과 같이 말이다.
OrderSpecifier<?> orderSpecifier;
switch (sortBy) {
case CREATED_AT -> {
if (sortDirection == Direction.ASC) {
orderSpecifier = deliveryAddress.createdAt.asc();
} else {
orderSpecifier = deliveryAddress.createdAt.desc();
}
}
case UPDATED_AT -> {
if (sortDirection == Direction.ASC) {
orderSpecifier = deliveryAddress.updatedAt.asc();
} else {
orderSpecifier = deliveryAddress.updatedAt.desc();
}
}
default -> orderSpecifier = deliveryAddress.createdAt.desc();
}
List<DeliveryAddress> resultList = queryFactory
.selectFrom(deliveryAddress)
.where(builder)
.offset(pageable.getOffset())
.orderBy(orderSpecifier)
.limit(pageable.getPageSize())
.fetch();
이 때 createdAt이나 updatedAt과 같은 공통 필드를 가지고 정렬을 수행해야 할 때가 있다. 그런데 OrderSpecifier는 deliveryAddress.createdAt.asc()와 같이 QClass에서 가져오기 때문에 엔티티마다 저 switch 코드를 작성해야 하는 불편함이 있었다.
그래서 이를 해결하기 위해 공통 필드들, 즉 BaseEntity에 있는 필드들에 대한 OrderSpecifier를 제공해주는 함수를 구현하였다.
public static OrderSpecifier<?> getCommonOrderSpecifier(
EntityPathBase<? extends BaseEntity> qClass,
Sort.Direction sortDirection,
CommonSortBy sortBy) {
PathBuilder<BaseEntity> entityPath = new PathBuilder<>(BaseEntity.class,
qClass.getMetadata());
switch (sortBy) {
case CREATED_AT -> {
return sortDirection == Sort.Direction.ASC
? entityPath.getDateTime("createdAt", LocalDateTime.class).asc()
: entityPath.getDateTime("createdAt", LocalDateTime.class).desc();
}
case UPDATED_AT -> {
return sortDirection == Sort.Direction.ASC
? entityPath.getDateTime("updatedAt", LocalDateTime.class).asc()
: entityPath.getDateTime("updatedAt", LocalDateTime.class).desc();
}
default -> throw new IllegalArgumentException("지원되지 않는 정렬 조건: " + sortBy);
}
}
내가 최종적으로 작성한 코드는 위와 같다 하나씩 살펴보자.
QClass들을 보면 모두 다음과 같이 EntityPathBase<T>를 상속하고 있다.
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QDeliveryAddress extends EntityPathBase<DeliveryAddress> {
그래서 EntityPathBase<T>를 이용하여 QClass들에 대한 공통적인 처리가 가능하다. EntityPathBase<? extends BaseEntity> qClass로 타입을 받으면 BaseEntity를 상속한 엔티티들에 대한 QClass 타입이 되는 것이다.
그리고 PathBuilder<T> 역시 EntityPathBase<T>를 상속하고 있으며 QueryDSL에서 문자열로 속성에 접근할 수 있도록 해주는 클래스이다.
public class PathBuilder<T> extends EntityPathBase<T> {
...
public PathBuilder(Class<? extends T> type, PathMetadata pathMetadata) {
this(type, pathMetadata, PathBuilderValidator.DEFAULT);
}
}
EntityPathBase<T>의 getMetadata()는 PathMetadata를 반환하는 함수로 PathMetadata는 엔티티의 경로를 표현하는데 필요한 메타 데이터를 다루는 클래스이다.
엔티티 클래스와 PathMetadata 통해 PathBuilder<T>를 초기화 할 수 있다. 우리는 BaseEntity의 필드들에 대한 접근이 필요하므로 BaseEntity.class와 파라미터로 받은 QClass의 PathMetadata를 전달해주면 된다.
entityPath.getDateTime("createdAt", LocalDateTime.class).asc()
그렇게 초기화 한 객체에서 위와 같이 필드에 접근 할 수 있다. createdAt이 LocalDateTime 타입이므로getDateTime()을 사용해주었고, 필드명 문자열과 필드의 타입을 파라미터로 전달해주면 된다. 그 후 asc()와 같이 QClass의 필드에 접근이 가능하다!
OrderSpecifier<?> orderSpecifier = PageUtils.getCommonOrderSpecifier(deliveryAddress,
sortDirection, sortBy);
OrderSpecifier<?> orderSpecifier = PageUtils.getCommonOrderSpecifier(order, sortDirection,
sortBy);
그럼 이제 QClass마다 복잡한 switch 구문을 작성할 필요 없이 위와 같이 QClass만 파라미터로 전달해주면 간단히 OrderSpecifier를 초기화 할 수 있다!!