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);
}
}
위 코드를 실행한 결과, 내가 설정한 방식대로 쿼리가 나갔음을 확인할 수 있었다.
아래 그림은 나간 쿼리이다.