JPA에 대한 공부를 진행하며 프로젝트를 진행하던 도중 복잡한 쿼리를 짜야하는 상황이 왔다.
MyBatis로 진행하게 되면 대충 다음과 같은 동적쿼리로 실행해야 원하는 값을 뽑아올 수 있었다. 여기에 부가적으로 더 많은 쿼리문이 들어가야 했고 불가능한건 아니지만 Mybatis는 Compile시점에서 Error를 도출하기 힘들기 때문에 JPA로 전환해보고자 했다.
대략적인 패키지 구조는 다음과 같으며 반환POJO로쓸 Dto,
Table entity를 정의해놓고 mapping에는 Entity<-> Dto를 변환시켜줄 mapstruct를 정의해놓았다.
위의 해당 동적 쿼리를 Querydsl을 이용해 변환시킨결과
다음과 같이 변했고 하나의 조건절에 대한 단위테스트가 가능해졌다.
public class OneononeInquiryRepositoryUtil {
static QOneononeInquiry oneononeInquiry = QOneononeInquiry.oneononeInquiry;
public static BooleanExpression eqServiceType(String serviceType){
if (StringUtils.equals("", serviceType)){
return null;
}
return oneononeInquiry.channelDivisioncode.eq(serviceType);
}
public static BooleanExpression inCategoryTypeList(List<String> categoryList){
if(categoryList.isEmpty()){
return null;
}
return oneononeInquiry.customercenterCategoryCode.in(categoryList);
}
public static BooleanExpression containSearchCategory(String searchCategory,String searchKeyword){
if(StringUtils.equals("SR01", searchCategory)){
return oneononeInquiry.registProcessorId.contains(searchKeyword);
}
if(StringUtils.equals("SR21", searchCategory)){
return oneononeInquiry.title.contains(searchKeyword);
}
return (oneononeInquiry.registProcessorId.contains(searchKeyword).or(oneononeInquiry.title.contains(searchKeyword)));
}
public static BooleanExpression betweenReferenceDay(String startDay, String endDay){
if(!StringUtils.equals(startDay, "")){
return oneononeInquiry.registProcessDayandtime.between(startDay,endDay);
}
return oneononeInquiry.registProcessDayandtime.lt(endDay);
}
public static OrderSpecifier<String> orderByReferenceDayFirst(String referenceDay){
StringPath aliasQuantity = Expressions.stringPath("lastAnswerDay");
if(StringUtils.equals("최종문의일",referenceDay)){
return aliasQuantity.desc();
}
return oneononeInquiry.registProcessDayandtime.desc();
}
public static OrderSpecifier<String> orderByReferenceDaySecond(String referenceDay){
StringPath aliasQuantity = Expressions.stringPath("lastAnswerDay");
if(StringUtils.equals("최종문의일",referenceDay)){
return aliasQuantity.desc();
}
return oneononeInquiry.registProcessDayandtime.desc();
}
}
Util클래스로 빼내어 정적 메서드로 사용할 수 있게 만드는것이 리팩토링, 관리면에서 더 편리하다고 생각했다. 코드를 보면 조건절을 이용해 원하는 쿼리를 반환하거나 Null을 반환해 해당 쿼리가 실행하지 않도록 할 수 있다.
TDD 관점에서 개발시 사용하지 않을 이유가 없다고 생각한다. 복잡한 Domain에서 JPA사용이 힘들시 Mybatis와 혼용하여 사용하면 더 좋을 것 같다.