인프런 커뮤니티 질문&답변

olive3three님의 프로필 이미지
olive3three

작성한 질문수

실전! Querydsl

스프링 데이터 페이징 활용3 - 컨트롤러 개발

querydsl orderspecifier 조건 분기

해결된 질문

작성

·

728

·

수정됨

0

private List<OrderSpecifier> getOrderSpecifier() {

        List<OrderSpecifier> orderSpecifiers = new ArrayList<>();

        //승인 안된 관리자 우선 조회
        orderSpecifiers.add(new OrderSpecifier<>(Order.ASC, admin.isAuthorized));

        orderSpecifiers.add(new OrderSpecifier(
                new CaseBuilder()
                        .when(admin.isAuthorized.eq("FALSE")).then(admin.createdAt.asc()) 
                        .when(admin.isAuthorized.eq("TRUE")).then(admin.createdAt.desc()) 
        ));

        return orderSpecifiers;
    }

아래와 같은 order 순서로 정렬하려 하는데 2,3 조건을 나누는 부분이 잘 되지 않아 질문 남깁니다. CaseBuilder 대신 다른 방법이 있나요?

  1. 승인 받지 못한 관리자(isAuthorized = 'FALSE') 먼저 조회

  2. isAuthorized = 'FALSE' 인 경우에는 createdAt asc

  3. isAuthorized = 'TRUE' 인 경우에는 createdAt desc

답변 3

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. olive3three님

우선 case 쿼리를 작성할 때 항상 else 조건을 생각해야 합니다.

현재 else 조건을 넣지 않았는데요. 이 경우 조건을 만족하지 않으면 숫자는 0, 나머지는 null이 됩니다.

참고로 Querydsl에서는 otherwise()를 넣어주어야 합니다.

 

2가지 정도 해결 방안을 말씀드릴게요.

먼저 적어주신 내용으로는 다음과 같이 쿼리를 작성하시면 됩니다.

List<Admin> result = queryFactory.selectFrom(admin)
        .orderBy(new CaseBuilder()
                        .when(admin.isAuthorized.eq("FALSE")).then(0)
                        .when(admin.isAuthorized.eq("TRUE")).then(1)
                        .otherwise(2).asc(),
                new CaseBuilder()
                        .when(admin.isAuthorized.eq("FALSE")).then(admin.createdAt)
                        .otherwise(LocalDateTime.now()).asc(),
                new CaseBuilder()
                        .when(admin.isAuthorized.eq("TRUE")).then(admin.createdAt)
                        .otherwise(LocalDateTime.now()).desc()
        )
        .fetch();

 

다음과 같이 날짜 자체를 숫자로 변경한 다음, 숫자로 변경한 날짜를 정렬 조건으로 사용해 한번에 처리하는 방식으로 문제를 해결할 수도 있습니다.

// 날짜를 문자로 변경한 다음에 문자를 양수로 변경
StringTemplate formattedDateAsc = Expressions.stringTemplate(
        "CAST(FORMATDATETIME({0}, {1}) AS LONG)"
        , admin.createdAt
        , ConstantImpl.create("yyyyMMddHHmmss")); // 양수

// 날짜를 문자로 변경한 다음에 문자를 음수로 변경
StringTemplate formattedDateDesc = Expressions.stringTemplate(
        "CAST(FORMATDATETIME({0}, {1}) AS LONG)"
        , admin.createdAt
        , ConstantImpl.create("-yyyyMMddHHmmss")); // 음수

List<Admin> result = queryFactory.selectFrom(admin)
        .orderBy(new CaseBuilder()
                .when(admin.isAuthorized.eq("FALSE")).then(formattedDateDesc)
                .when(admin.isAuthorized.eq("TRUE")).then(formattedDateAsc)
                .otherwise(formattedDateAsc).asc())
        .fetch();

감사합니다.

 

olive3three님의 프로필 이미지
olive3three
질문자

답변 감사합니다!!

0

안녕하세요. olive3three님, 공식 서포터즈 David입니다.

나누는 부분이 어떻게 잘 안되는지 조금 더 구체적으로 질문을 남겨주실 수 있으실까요?🙂

감사합니다.

olive3three님의 프로필 이미지
olive3three
질문자

image

IS_AUTHORIZED = 'FALSE'인 경우에는 CREATED_AT이 내림차순으로 IS_AUTHORIZED = 'TRUE'인 경우에는 CREATED_AT이 오름차순으로 정렬되었으면 합니다.

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. olive3three님

정확히 어떤 문제인지 저희가 이해하기 위해서 추가 정보가 필요합니다. 먼저 기대하는 내용이 SQL에서 작동하는지 확인이 필요합니다.
다음 내용을 자세히 남겨주세요 🙂

  1. 테이블 DDL

  2. 예제 insert DML

  3. 생각하시는 조회 SQL

  4. SQL 실행의 조회 결과

감사합니다.

olive3three님의 프로필 이미지
olive3three
질문자

  1. Admin 테이블 DDL
    DROP TABLE IF EXISTS ADMIN CASCADE; CREATE TABLE ADMIN ( ADMIN_ID BIGINT UNSIGNED GENERATED BY DEFAULT AS IDENTITY, LOGIN_ID VARCHAR(20) NOT NULL, PASSWORD VARCHAR(100) NOT NULL, NAME VARCHAR(5) NOT NULL, EMAIL VARCHAR(45) NOT NULL, IS_AUTHORIZED VARCHAR(10) NOT NULL, AUTHORITY VARCHAR(15) NOT NULL, CREATED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, MODIFIED_AT TIMESTAMP, PRIMARY KEY (ADMIN_ID), UNIQUE KEY ADMIN_LOGIN_ID_UNIQUE (LOGIN_ID), UNIQUE KEY ADMIN_EMAIL_UNIQUE (EMAIL) );

  2. 예제 insert DML

INSERT INTO TEST VALUES(1, 'admin1', 'admin1234!', '관리자1', 'admin1@gmail.com', 'TRUE', 'ROLE_ADMIN', '2024-07-11 14:30:00', '2024-07-11 14:30:00');

INSERT INTO TEST VALUES(2, 'admin2', 'admin1234!', '관리자2', 'admin2@gmail.com', 'TRUE', 'ROLE_ADMIN', '2024-07-12 14:30:00', '2024-07-12 14:30:00');

INSERT INTO TEST VALUES(3, 'admin3', 'admin1234!', '관리자3', 'admin3@gmail.com', 'FALSE', 'ROLE_ANONYMOUS', '2024-07-13 14:30:00', '2024-07-13 14:30:00');

INSERT INTO TEST VALUES(4, 'admin4', 'admin1234!', '관리자4', 'admin4@gmail.com', 'FALSE', 'ROLE_ANONYMOUS', '2024-07-14 14:30:00', '2024-07-14 14:30:00');

  1. 조회 SQL

SELECT * FROM TEST

ORDER BY

CASE

WHEN is_authorized = 'FALSE' THEN 0

WHEN is_authorized = 'TRUE' THEN 1

END,

CASE

WHEN is_authorized = 'FALSE' THEN created_at

END ASC,

CASE

WHEN is_authorized = 'TRUE' THEN created_at

END DESC;

  1. SQL 실행의 조회 결과

image
3번의 SQL문을 QueryDsl로 바꾸고 싶습니다. 또한 SQL문이 올바르게 짠건지 궁금합니다.

김영한님의 프로필 이미지
김영한
지식공유자

본문에 추가로 답글 달아두었습니다 :)

0

안녕하세요, 인프런 AI 인턴입니다. olive3three님의 질문과 관련해, 주어진 조건에 따라 특정 엔티티의 리스트를 정렬하는 방법에 대해 질문하셨군요. 여러분이 질문하신 OrderSpecifier를 사용하여 isAuthorized의 값에 따라 다른 정렬 순서를 적용하는 방법에 대해 말씀드리겠습니다.

질문하신 내용과 유사한 상황에 대해 처리하는 방법을 설명한 기존 답변은 아래 링크에서 찾을 수 있습니다:

  • “정렬 조건에 대해 질문이 있습니다.”에서는 enum 값을 기준으로 커스텀 정렬을 해야 할 때 CaseBuilder를 사용하는 방법을 설명하고 있습니다. 여러분의 경우에도 CaseBuilder를 이용하여 isAuthorized 값에 따라 다른 정렬 기준을 적용하는 것이 가능합니다. 이 방법은 여러분이 사용하고자 하는 조건 분기에 맞게 조금만 변형하면 적용할 수 있을 것으로 보입니다.

위의 답변에서 CaseBuilder를 사용하여 조건에 따라 다른 OrderSpecifier를 생성하는 방식을 다루고 있으며, 이 방법은 여러분이 시도하고자 하는 조건 분기에 맞게 적응할 수 있습니다. 주어진 코드 예제에서는 enum 값을 기준으로 정렬 순서를 정의하는 예를 다루었으나, 해당 방식을 isAuthorized 값에 기반하여 수정하는 것도 가능합니다. 예를 들면, isAuthorized 값에 따라 다른 createdAt 정렬 순서를 적용하려면, 여러분의 CaseBuilder 로직을 유지하면서, 적절한 반환 타입을 활용해야 합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

olive3three님의 프로필 이미지
olive3three

작성한 질문수

질문하기