월별 좋아요한 일정 가져오기 중 querydsl 작성하면서..

jungnoeun·2023년 2월 25일
0

kiri

목록 보기
13/13

고민의 내용

A or (B and C)를 하고 싶음.
return scrap.startYear.loe(year) ? 함수() : null
위와 같은 방법을 처음 시도했으나,
BooleanExpression을 boolean으로 바꾸지 못하므로 애를 먹고 있었음.
아래의 문답을 참고하여, where 다중 파라미터를 구현하였다.




구체적으로 참고한 내용

(a and b and c) or d 조건을 동적 쿼리 - Where 다중 파라미터 사용을 통해 구현한다면

BooleanExpression "a, b, c 조건이냐 아니면 d냐 체크"(구분할 수 있는 파라미터 i) {
  return "a, b, c 체크"(i).or("d 조건 체크"(i))
}

BooleanExpression "a, b, c 체크"(i) {
  return a.and(b).and(c);
}

BooleanExpression "d 체크"(i) {
  return d;
}



위의 예시를 참고하여 원하는 쿼리가 나가도록 코드를 작성하였다.

실패 코드

위의 예시를 참고하기전 내가 작성한 실패코드는 아래와 같다.

@RequiredArgsConstructor
public class ScrapRepositoryImpl implements ScrapCustomRepository {

    private final JPAQueryFactory jpaQueryFactory;

    public List<ScrapResCal> findScrapsByYearAndMonth(Integer year, Integer month) {

        List<ScrapResCal> result = jpaQueryFactory.select(new QScrapResCal(
                        post.id,
                        post.title,
                        post.organizer,
                        post.school,
                        post.local,
                        post.event,
                        scrap.startScrapTime,
                        scrap.endScrapTime))
                .from(scrap)
                .leftJoin(scrap.post, post)
                .leftJoin(scrap.member, member)
                .where(compareSY(), compareSM, compareFY(), compareFM )
//                .where(compareSY(year), compareSM(month), compareFY(year), compareFM(month))
//                .offset(0)
//                .limit(20)
                .fetch();

        return result;
    }


    /**
     * start year <= now year (2022 < 2023)
     * - start month <= now month (1 < 2)
     * - start month > now month (12 > 1) => 문제
     *
     * now year <= finish year (2023 < 2024)
     * - now month <= finish month (2 <= 3)
     * - now month > finish month (12 > 1) => 문제
     *
     * 그니까, year month 가 다 start보다 크거나 같고, finish보다 작거나 같다
     * 로 끝나면 안됨.
     *
     * nowY > startY 이면,
     *
     *
     */

    private BooleanExpression compareSY(Integer year, Integer month) {
        if(year == null) {
            return null;
        }
        return scrap.startYear.loe(year).and(compareSM(month));
    }
    private BooleanExpression compareSM(Integer month) {
        if(month == null) {
            return null;
        }
        return scrap.startMonth.loe(month);
    }

    private BooleanExpression compareFY(Integer year) {
        if(year == null) {
            return null;
        }
        return scrap.finishYear.goe(year);
    }
    private BooleanExpression compareFM(Integer month) {
        if(month == null) {
            return null;
        }
        return scrap.finishMonth.goe(month);
    }
}

실패 이유

하지만 위와 같이 작성할 경우,
시작일이 2022.12.XX이고, 현재가 2023.02.XX 일이고, 끝나는 날이 2023.03.XX일과 같이 시작년도와 현재연도가 다를 경우,
쿼리는 시작년와 현재연도를 비교하여,
compareSY(Integer year, Integer month)의 2022<=2023 조건을 만족하지만,
compareSM(Integer month) 의 12<=2 을 만족하지 못하여,
해당 스크랩 게시글을 가져와야 하지만 가져오지 못한다.

그래서 startYear와 nowYear가 같거나 작은 경우를 한번에 처리하지 않고,
이 두가지 경우를 따로 처리하도록 하였다.
1. startYear<nowYear 인 경우, month 비교는 따로 하지 않는다.
2. startYear==nowYear 인 경우, month 비교는 필수이므로, startMonth<=nowMonth를 반드시 만족해야 한다.
3. 위 두가지 경우를 만족하지 못하거나, startYear>nowYear 인 경우, null을 반환해 쿼리가 만족되지 못하게 한다.

이 방법은 finishTime 과 now 시간에 똑같이 적용하였다. 그렇게 해서 작성한 코드는 아래과 같다.

성공 코드

@RequiredArgsConstructor
public class ScrapRepositoryImpl implements ScrapCustomRepository {

    private final JPAQueryFactory jpaQueryFactory;

    public List<ScrapResCal> findScrapsByYearAndMonth(Integer year, Integer month) {

        List<ScrapResCal> result = jpaQueryFactory.select(new QScrapResCal(
                        post.id,
                        post.title,
                        post.organizer,
                        post.school,
                        post.local,
                        post.event,
                        scrap.startScrapTime,
                        scrap.endScrapTime))
                .from(scrap)
                .leftJoin(scrap.post, post)
                .leftJoin(scrap.member, member)
                .where(compareStart(year, month), compareFinish(year, month))
//                .where(compareSY(year), compareSM(month), compareFY(year), compareFM(month))
//                .offset(0)
//                .limit(20)
                .fetch();

        return result;
    }


    /**
     * start year <= now year (2022 < 2023)
     * - start month <= now month (1 < 2)
     * - start month > now month (12 > 1) => 문제
     *
     * now year <= finish year (2023 < 2024)
     * - now month <= finish month (2 <= 3)
     * - now month > finish month (12 > 1) => 문제
     *
     * 그니까, year month 가 다 start보다 크거나 같고, finish보다 작거나 같다
     * 로 끝나면 안됨.
     *
     * nowY > startY 이면,
     *
     *
     */


    private BooleanExpression compareStart(Integer year, Integer month) {
        if(year == null || month == null) {
            return null;
        }
        return compareLwSY(year).or(compareSYSM(year, month));
    }

    private BooleanExpression compareLwSY(Integer year) {
        return scrap.startYear.lt(year);
    }
    private BooleanExpression compareSYSM(Integer year, Integer month) {
        return compareEqSY(year).and(compareLwSM(month));
    }

    private BooleanExpression compareEqSY(Integer year) {
        return scrap.startYear.eq(year);
    }
    private BooleanExpression compareLwSM(Integer month) {
        return scrap.startMonth.loe(month);
    }

    private BooleanExpression compareFinish(Integer year, Integer month) {
        if(year == null || month == null) {
            return null;
        }
        return compareGtFY(year).or(compareFYFM(year, month));
    }

    private BooleanExpression compareGtFY(Integer year) {
        return scrap.finishYear.gt(year);
    }

    private BooleanExpression compareFYFM(Integer year, Integer month) {
        return compareEqFY(year).and(compareGtFM(month));
    }

    private BooleanExpression compareEqFY(Integer year) {
        return scrap.finishYear.eq(year);
    }

    private BooleanExpression compareGtFM(Integer month) {
        return scrap.finishMonth.goe(month);
    }



}


위 코드를 실행한 결과, 내가 설정한 방식대로 쿼리가 나갔음을 확인할 수 있었다.




아래 그림은 나간 쿼리이다.




참고
https://www.inflearn.com/questions/518473/querydsl-and-or-%EC%B2%98%EB%A6%AC-%EC%A7%88%EB%AC%B8%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4

profile
개발자

0개의 댓글