[우아한테크코스] 7월 10일 TIL
[Java] Builder 패턴
빌더패턴이란, 객체 생성을 깔끔하고 유연하게 하기 위한 기법이다.
필수 인자를 받는 생성자를 만들고, 선택 인자들을 점진적으로 체이닝하여 받을 수 있도록 한다.
객체 내에 Builder 클래스를 만들고 선택 인자들을 부생성자로 추가해둔다.
최종 객체 생성은 다음과 같이 할 수 있다.
Reservation reservation = new Reservation
.Builder(123, 345)
.date(678)
.description(890)
.build();
이와 같이 구현함으로써 각 인자가 어떤 의미인지 알기 쉽고 immutable한 객체로 만들 수 있다.
Lombok을 사용할 경우 더욱 간단히 빌더패턴을 적용할 수 있다.
@Builder
어노테이션을 적용하면 아래와 같이 선언만 해도 빌더를 사용할 수 있다.
선언부
@Builder
public class Reservation {
private final int name;
private final int date;
private final int description;
}
구현부
Reservation.ReservationBuilder builder = Reservation.builder();
builder.name(345);
builder.date(678);
builder.description(123);
Reservation reservation = builder.build();
Reservation reservation = Reservation.builder()
.name(123)
.date(456)
.description(789)
.build();
[Spring] custom validator 만들기
ConsistentDate 어노테이션
@Constraint(validatedBy = ConsistentDateValidator.class)
@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Documented
public @interface ConsistentDate {
String message() default
"종료 시간을 확인해주세요.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// String startTime();
//
// String endTime();
//
// @Target({ElementType.TYPE})
// @Retention(RetentionPolicy.RUNTIME)
// @interface List {
// Constraint[] value();
// }
}
ConsistentDateValidator 구현부
public class ConsistentDateValidator
implements ConstraintValidator<ConsistentDate, Object[]> {
// private LocalDateTime startTime;
// private LocalDateTime endTime;
//
// @Override
// public void initialize(ConsistentDate constraintAnnotation) {
// this.startTime = LocalDateTime.parse(constraintAnnotation.startTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// this.endTime = LocalDateTime.parse(constraintAnnotation.endTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// }
//
// @Override
// public boolean isValid(Object value, ConstraintValidatorContext context) {
// if(startTime.isBefore(LocalDateTime.now())) {
// throw new ImpossibleStartTimeException();
// }
//
// return startTime.isBefore(endTime);
// }
@Override
public boolean isValid(
Object[] value,
ConstraintValidatorContext context) {
if (value[0] == null || value[1] == null) {
return true;
}
if (((LocalDateTime) value[0]).isBefore(LocalDateTime.now())) {
throw new ImpossibleStartTimeException();
}
return ((LocalDateTime) value[0]).isAfter(LocalDateTime.now())
&& ((LocalDateTime) value[0]).isBefore((LocalDateTime) value[1]);
}
}
등록한 타입에 맞춰서 어노테이션 달아서 적용
String 타입으로 어노테이션에 넣은 경우는 @ConsistentDate(startTime = "startDateTime", endTime = "endDateTime")
, 그냥 메서드 위에 단 경우에는 @ConsistentDate
로 적용
[JPA] and/or combination
필요한 것은 spaceId와 매칭되면서 startTime과 endTime 사이에도 매칭되는 것이었다. 기호로는 (spaceId && StartTimeBetween) || (spaceId &&EndTimeBetween)
이다.
이를 위해서 existBySpaceIdAndStartTimeBetweenOrEndTimeBetween
을 하였다.
하지만 이렇게 되면 (spaceId && StartTimeBetween)||EndTimeBetween
을 보는 것이었다.
검색을 해봤지만 QueryDsl을 이용하는 방안밖에 찾지 못했고 결국에 jpa 메서드를 두번 날리고 그 둘의 결과를 OR 연산 하는 방안으로 수정했다.
existsBySpaceIdAndStartTimeBetween || existsBySpaceIdAndEndTimeBetween
그리고 나는 처음에는 내가 가진 start와 end 사이에 DB에 존재하는 start나 end가 있으면 될 것이라고 생각했는데 JPA의 Between 쿼리문은 내가 가진 start와 end 사이에 DB에 존재하는 start가 있는지 보고, end가 있는지 보는 일이 따로 일어나야 했다. 그래서 하나의 between ? 쿼리문마다 두개의 인자를 전달해주어야 한다.