Project/Management

[Management] QueryDSL : orderBy() - MultiSort 동적으로 적용

lakelight 2022. 11. 8. 18:39
728x90
반응형
Frontend 요청

페이징을 적용하고 나니, 전체 데이터를 응답해주지 않기 때문에
프론트에서 속성 별 정렬이 안되기 때문에 페이징이 적용된 부분에는
백엔드에서 동적으로 Multi Sort를 적용해달라는 요청을 받았습니다.

 

Frontend의 Request Body
/*Request Body JSON FORMAT*/
{
    "page":Integer,
    "size":Integer,
    "sortBy":String[], // 정렬이 필요한 필드 값
    "sortDesc":Boolean[] // sortBy가 DESC인지 ASC인지 Boolean값을 통해 알려줍니다.
}

 

Backend 처리

1. 기존 코드

/*Entity에 실제 엔티티의 이름 적용*/
public Page<Entity> EntitySearch(SearchCondition condition, Pageable pageable) {

    QueryResults<Entity> results = queryFactory
            .select(QEntity.entity)
            .from(QEntity.entity)
            .where(
                    propertyEq(condition.getProperty()),
                    ...
            )
            .orderBy(**Multi Sort**)
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetchResults();

    List<Entity> content = results.getResults();
    Long total = results.getTotal();

    return new PageImpl<>(content, pageable, total);
}

 

2. 먼저 orderBy 파라미터를 알아보았습니다.

orderBy Reference

orderBy: Add ordering of the result as an varargs array of order expressions. Use asc() and desc() on numeric, string and other comparable expression to access the OrderSpecifier instances.

OrderSpecifier 인스턴스가 필요하다는 것을 알았고, orderBy(OrderSpecifier, OrderSpecifier ...) 을 통해 MultiSort가 가능하다는 것을 인지했습니다.

그렇다면 동적으로 OrderSpecifier을 파라미터로 넣을 수 있다면 요구사항을 해결할 수 있습니다.

 

3. orderBy 파라미터 OrderSpecifier를 동적으로 Input 하기 위한 방법

Request로 받는 sortBy와 sortDesc를 하나씩 꺼내서 List<OrderSpecifier> 에 저장하고, orderBy 배열의 형태로 반환한다면 가능할 것 같다고 생각하여 바로 실행해보았습니다.

 

4. 기존코드에서 필요한 부분 추가

String[] sortBy = condition.getSortBy(); //# Request Body에서 Get
Boolean[] sortDesc = condition.getSortDesc(); //# Request Body에서 Get
List<OrderSpecifier> orderBy = new LinkedList<>(); //# sortBy의 size를 모르기 때문에 List로 선언

//entityMultiSortFilter(sortBy, sortDesc, orderBy); # 구현 예정

 

5. entityMultiSortFilter() 구현

private static void entityMultiSortFilter(String[] sortBy, Boolean[] sortDesc, List<OrderSpecifier> orderBy) {
    for (int i = 0; i < sortBy.length; i++) {
        String key = sortBy[i];
        Boolean value = sortDesc[i];

        switch (key) {
            case "property":
                if (value) // true 이면 DESC
                    orderBy.add(new OrderSpecifier(Order.DESC, Expressions.path(Object.class, QEntity.entity, "property")));
                else // false 이면 ASC
                    orderBy.add(new OrderSpecifier(Order.ASC, Expressions.path(Object.class, QEntity.entity, "property")));
                break;
                ...
                ... #property에 속성 값 Input
                ...
            case "property":
            if (value) // true 이면 DESC
                orderBy.add(new OrderSpecifier(Order.DESC, Expressions.path(Object.class, QEntity.entity, "property")));
            else // false 이면 ASC
                orderBy.add(new OrderSpecifier(Order.ASC, Expressions.path(Object.class, QEntity.entity, "property")));
            break;
            default:
                break;
        }
    }
}

 

6. orderBy에 List를 Array형태로 Input

public Page<Entity> EntitySearch(EntityCondition condition, Pageable pageable) {
    String[] sortBy = condition.getSortBy();
    Boolean[] sortDesc = condition.getSortDesc();
    List<OrderSpecifier> orderBy = new LinkedList<>();
    entitySearchMultiSortFilter(sortBy, sortDesc, orderBy);

    QueryResults<Entity> results = queryFactory
            .select(QEntity.entity)
            .from(QEntity.entity)
            .where(

            )
            .orderBy(orderBy.stream().toArray(OrderSpecifier[]::new))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetchResults();

    List<Entity> content = results.getResults();
    Long total = results.getTotal();

    return new PageImpl<>(content, pageable, total);
}
orderBy부분 orderBy.stream().toArray(OrderSpecifier[]::new) 을 통해 리스트를 배열 형태로 반환

 

7. 전체 코드

@Repository
@RequiredArgsConstructor
public class EntityRepositoryQueryDsl {

    private final JPAQueryFactory queryFactory;

    public Page<Entity> EntitySearch(EntityCondition condition, Pageable pageable) {
        String[] sortBy = condition.getSortBy();
        Boolean[] sortDesc = condition.getSortDesc();
        List<OrderSpecifier> orderBy = new LinkedList<>();
        entitySearchMultiSortFilter(sortBy, sortDesc, orderBy);

        QueryResults<Entity> results = queryFactory
                .select(QEntity.entity)
                .from(QEntity.entity)
                .where(
                    propertyEq(condition.getProperty()),
                    ...
                )
                .orderBy(orderBy.stream().toArray(OrderSpecifier[]::new))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetchResults();

        List<Entity> content = results.getResults();
        Long total = results.getTotal();

        return new PageImpl<>(content, pageable, total);
    }

    private static void entityMultiSortFilter(String[] sortBy, Boolean[] sortDesc, List<OrderSpecifier> orderBy) {
        for (int i = 0; i < sortBy.length; i++) {
            String key = sortBy[i];
            Boolean value = sortDesc[i];

            switch (key) {
                case "property":
                    if (value) // true 이면 DESC
                        orderBy.add(new OrderSpecifier(Order.DESC, Expressions.path(Object.class, QEntity.entity, "property")));
                    else // false 이면 ASC
                        orderBy.add(new OrderSpecifier(Order.ASC, Expressions.path(Object.class, QEntity.entity, "property")));
                    break;
                    ...
                    ... #property에 속성 값 Input
                    ...
                case "property":
                if (value) // true 이면 DESC
                    orderBy.add(new OrderSpecifier(Order.DESC, Expressions.path(Object.class, QEntity.entity, "property")));
                else // false 이면 ASC
                    orderBy.add(new OrderSpecifier(Order.ASC, Expressions.path(Object.class, QEntity.entity, "property")));
                break;
                default:
                    break;
            }
        }
    }
}

 

 

마무리

오늘은 QueryDSL에서 Multi Sort를 구현해보았습니다.
제가 생각한 방법과 다르게 더 좋은 방법이 있다면 댓글 남겨주시면 감사하겠습니다.

오늘도 포스팅 읽어주셔서 감사합니다.

728x90
반응형