Querydsl μ κΈ°λ³Έ μ¬μ©λ²μ μκ°νμ§ μμ΅λλ€.
Querydsl μ νμ΅ μ€μ μμ±ν κΈμ΄κΈ° λλ¬Έμ μκ°ν λ°©λ²μ΄ μ΅κ³ μ λ°©λ²μ΄ μλ μ μμμ μλ €λ립λλ€πβοΈ
κ°λ°νκ²½
JDK 17, Querydsl 5.0.0, Spring boot 3.x
βμλ‘
μ½λμ€μΏΌλ μμ΄λΉμ€λΉ ν΄λ‘ νλ‘μ νΈ μ€, μ¬μ©μ 쑰건μ λ°λΌ μμκ° κ²μλλ κΈ°λ₯μ ꡬννκ³ μ νμ΅λλ€.
μ¬μ©μκ° 6/19 ~ 6/20 λ₯Ό μμ κ²μ 쑰건μΌλ‘ λ±λ‘νλ©΄,
μλ²μμλ 6/19 ~ 6/20 κΉμ§ μ΄μ© κ°λ₯ ν μμ ID(μμ½ λμ΄μμ§ μμ μμ ID) λ₯Ό μ°Ύμ΅λλ€.
μλ₯Ό λ€μ΄,
- μμ IDκ° 1μΈ μμμ `6/19 PM15:00 ~ 6/20 AM11:00` μΈ μμ½μ΄ μλ€κ³ κ°μ ν©λλ€.
- λ§μ½, μ¬μ©μκ° 6/20 ~ 6/21 μ μ΄μ©κ°λ₯ν μμλ₯Ό μ°Ύλλ€λ©΄
- IDκ° 1μΈ μμμ 체ν¬μΈ μκ°μ PM 15:00, 체ν¬μμ μκ°μ AM 11:00 μ΄κΈ° λλ¬Έμ,
- IDκ° 1μΈ μμλ 6/20 ~ 6/21 μ μ΄μ©κ°λ₯νλλ‘ ν©λλ€.
- μ΄λ₯Ό μν΄, λ μ§λ λ μ§λλ‘ μκ°μ μκ°λλ‘ κ°κ° λΉκ΅νκ³ μ ν©λλ€.
βꡬν
- μμ½ μν°ν°μ 체ν¬μΈ,μμμ LocalDateTime μ λλ€.
* JPA μ ddl-auto κΈ°λ₯μ true λ‘ μ€μ νλ©΄ μν°ν°μ LocalDateTime μ Mysql μ DateTime μΌλ‘ μ μ₯μ΄ λ©λλ€.
```
@Entity
public class Reservation {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "STAY_ID", foreignKey = @ForeignKey(name = "FK_STAY_RESERVE_ID"))
private Stay stay;
@Column(name = "CHECK_IN")
private LocalDateTime checkIn;
@Column(name = "CHECK_OUT")
private LocalDateTime checkOut;
}
- μ¬μ©μκ° κ²μνλ λ μ§μ νμ μ LocalDate μ λλ€.
private BooleanExpression matchesCheckInAndCheckOut(LocalDate checkInDate, LocalDate checkOutDate)
μμ½μν°ν°μ LocalDateTime μ Mysql μ DateTime κ³Ό λμλκΈ° λλ¬Έμ(jpa μ ddl-auto=true λ‘ μ€μ νμ΅λλ€)
μμ μκ°ν μμλ₯Ό μΆ©μ‘±νκΈ° μν΄μλ DateTime μ Date μ Time μΌλ‘ μͺΌκ°μ,
λ μ§λ λ μ§λλ‘ μκ°μ μκ°λΌλ¦¬ κ°κ° λΉκ΅ν΄μΌ νμ΅λλ€.
κ·Έλμ, Querydsl μ Expressions λΌμ΄λΈλ¬λ¦¬μ μλ `timeTemplate` κ³Ό `dateTemplate` μ μ¬μ©νμ¬
dateTemplate μ μ¬μ©ν΄μ DateTime μ LocalDate λ‘,
timeTemplate μ μ¬μ©ν΄μ DateTime μ LocalTime μΌλ‘ λ°κΏ¨μ΅λλ€.
Expressions.timeTemplate μμ
// μμ½ checkInμκ°(νμ
: LocalDateTime) Time λ‘ νλ³ννμ¬, LocalTime μΌλ‘ λ§λλλ€.
TimeTemplate<LocalTime> reservationCheckInTime
= Expressions.timeTemplate(LocalTime.class, "CAST({0} AS TIME)", reservation.checkIn);
Expressions.dateTemplate μμ
// μμ½ checkout μκ°(νμ
: LocalDateTime)μ Date λ‘ νλ³ννμ¬, LocalDateλ‘ λ§λλλ€.
DateTemplate<LocalDate> reservationCheckOutDate
= Expressions.dateTemplate(LocalDate.class, "CAST({0} AS DATE)", reservation.checkOut);
Expressions.timeTemplate μ νλΌλ―Έν° μ΄ν΄νκΈ°
- LocalTime.class → timeTemplate(dateTemplate) μ κ²°κ³Όλ‘ μ¬ νμ μ μ μΈν©λλ€.
- `CAST({0} AS TIME)`→ Mysql μ DateTime μ Time μΌλ‘ νλ³ν ν©λλ€. {0}μ μ리 νμμλ‘, νμ μ 곡λ νλΌλ―Έν°(reservation.checkIn)λ‘ λ체λ©λλ€.
- reservation.checkIn → νλ³ν νκ³ μΆμ Mysql μ 컬λΌμ μ μΈν©λλ€.
μ 체 μ½λ
package com.airdnb.clone.domain.booking.repository;
import static com.airdnb.clone.domain.booking.entity.QBooking.booking;
import static com.airdnb.clone.domain.stay.entity.QStay.stay;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.DateTemplate;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.TimeTemplate;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class ReservationQuerydslImpl implements ReservationQuerydsl{
private final JPAQueryFactory jpaQueryFactory;
public List<Long> findUnavailableStayIdsByCheckInOut(LocalDate checkInDate, LocalDate checkOutDate) {
return jpaQueryFactory
.select(reservation.stay.id)
.distinct()
.from(reservation)
.where(matchesCheckInAndCheckOut(checkInDate, checkOutDate))
.fetch();
}
private BooleanExpression matchesCheckInAndCheckOut(LocalDate checkInDate, LocalDate checkOutDate) {
if (checkInDate == null || checkOutDate == null) {
return null;
}
if (checkInDate.isAfter(checkOutDate)) {
throw new IllegalArgumentException();
}
DateTemplate<LocalDate> reservationCheckInDate = Expressions.dateTemplate(LocalDate.class,
"CAST({0} AS DATE)"", reservation.checkIn);
TimeTemplate<LocalTime> reservationCheckInTime = Expressions.timeTemplate(LocalTime.class,
"CAST({0} AS TIME)"", reservation.checkIn);
DateTemplate<LocalDate> reservationCheckOutDate = Expressions.dateTemplate(LocalDate.class,
"CAST({0} AS DATE)"", reservation.checkOut);
TimeTemplate<LocalTime> reservationCheckOutTime = Expressions.timeTemplate(LocalTime.class,
"CAST({0} AS TIME)"", reservation.checkOut);
/*
* (μ΄λ―Έ μμ½ λ 체ν¬μΈ λ μ§ < κ²μν 체ν¬μμ λ μ§) or
* (μ΄λ―Έ μμ½λ 체ν¬μΈ λ μ§ == κ²μν 체ν¬μμ λ μ§) and (μ΄λ―Έ μμ½λ μμμ 체ν¬μΈ μκ° < ν΄λΉ μμμ 체ν¬μμ μκ°)
* and
* (μ΄λ―Έ μμ½λ 체ν¬μμ λ μ§ > κ²μν 체ν¬μΈ λ μ§) or
* (μ΄λ―Έ μμ½λ 체ν¬μμ λ μ§ == κ²μν 체ν¬μΈ λ μ§) and (μ΄λ―Έ μμ½λ μμμ 체ν¬μμ μκ° > ν΄λΉ μμμ 체ν¬μΈ μκ°)
* μ ν΄λΉλλ©΄ μμ½ λΆκ°λ₯ν μμμ ν΄λΉ.
*/
return reservationCheckInDate.lt(checkOutDate)
.or(reservationCheckInDate.eq(checkOutDate).and(reservationCheckInTime.lt(reservation.stay.checkOutTime)))
.and(reservationCheckOutDate.gt(checkInDate)
.or(reservationCheckOutDate.eq(checkInDate).and(reservationCheckOutTime.gt(reservation.stay.checkInTime))));
}
}
μ΄ μΈμλ μκ°κ³Ό λ μ§λ₯Ό λΉκ΅νκΈ° μν΄, Querydsl μ between κ³Ό stringTemplate λ μμΌλ μν©μ λ§κ² μ ννμλ©΄ λκ² μ΅λλ€.