OpenRun Project - Querydsl 사용해서 검색 쿼리 작성하기

Ango·2023년 8월 11일
0

Project

목록 보기
15/16

QueryDsl 적용 이유

  1. 동적 쿼리 작성에 유용함
  2. Complile time에 query error 검증

설정한 검색 조건

  1. 키워드 검색(일반적인 검색어)
    => 지금은 Like 문을 활용했는데 이후 검색 성능 향상을 위해 index나 full Text Index를 적용 해볼것이다. (+ Elasic Search)

  2. 카테고리, 가격 대, 오픈런 유무 필터 검색

  3. 가격대, 관심 상품 순 , 오픈런 시작 시작 시간 순 정렬

적용 방법

  1. ProductRepositoryCustom를 인터페이스로 만들어서 검색 쿼리를 작성할 매서드를 만들어 준다 .
public interface ProductRepositoryCustom {
    Page<AllProductResponseDto> searchAllProducts(ProductSearchCondition condition, Pageable pageable);
}
  1. ProductRepositoryImpl에 ProductRepositoryCustom 인터페이스를 상속받은 구현체를 만들어준다. 단, ${RepositoryName} + Impl 의 네이밍 컨벤션을 꼭 지켜야 한다.
@RequiredArgsConstructor
public class ProductRepositoryImpl implements  ProductRepositoryCustom{
    private final JPAQueryFactory queryFactory;

@Override
public Page<AllProductResponseDto> searchAllProducts(ProductSearchCondition condition, Pageable pageable)

쿼리문 분석
1) 생성자 방식의 Projection을 택하였다. (Record로 Dto를 만들어서 생성자 방식밖에 지원해주지 않았음)

List<AllProductResponseDto> content = queryFactory
               .select(Projections.constructor(AllProductResponseDto.class,
                       product.id,
                       product.productName,
                       product.productImage,
                       product.price,
                       product.mallName,
                       product.currentQuantity,
                       product.eventStartTime,
                       product.category,
                       product.totalQuantity,
                       product.wishCount
               )).from(product)

2) 위에서 말한 조건문을 생성한다 .

키워드 검색(일반적인 검색어)
카테고리, 가격 대, 오픈런 유무 필터 검색

BooleanBuilder 방식 대신에 쿼리문을 추가로 작성할 것과 코드의 가독성을 위해 Where 조건문 방식을 택했다.

.where(
        keywordContains(condition.getKeyword()),
        categoryEq(condition.getCategory()),
        isOpenRunEq(condition.getStatus()),
        isPriceBetween(condition.getLprice(), condition.getGprice())
)

각각의 매서드에서 조건문을 정의하고 where 문에 조건으로 준다.

< keywordContains >

 private BooleanExpression keywordContains(String keyword) {
     return hasText(keyword) ? product.productName.contains(keyword).or(product.mallName.contains(keyword)) : null;
 }

< categoryEq >

private BooleanExpression categoryEq(String category) {
    return hasText(category) ? product.category.eq(category) : null;
    }
 }

< isOpenRunEq >

private BooleanExpression isOpenRunEq(OpenRunStatus status) {
        if( status == null){
            return null;
        }

        return hasText(status.toString()) ? product.status.eq(status) : null;
}

< isPriceBetween >

    private BooleanExpression isPriceBetween(Integer lprice, Integer uprice) {

        if (lprice == null && uprice == null){
            return null;
        }else if(lprice == null){
            return product.price.loe(uprice);
        }else if(uprice == null){
            return product.price.goe(lprice);
        }

        return product.price.between(lprice, uprice);
    }
  1. 가격대, 관심 상품 순 , 오픈런 시작 시작 시간 순 정렬에 관련된 조건문 생성

< sortMethod >

    private OrderSpecifier<?> sortMethod(String sortBy, Boolean isAsc) {
        if(isAsc ==null){
            isAsc = true;
        }

        if(sortBy == null){
            return product.id.desc();
        }

        switch(sortBy){
            case "price" -> {
                return (isAsc == true) ? product.price.asc() : product.price.desc();
            }
            case "eventStartTime" -> {
                return (isAsc == true) ? product.eventStartTime.asc() : product.eventStartTime.desc();
            }
            case "wishCount" -> {
                return (isAsc == true) ? product.wishCount.asc() : product.wishCount.desc();
            }
            default -> {
                return product.id.desc();
            }
        }
    }
  1. ProductReposiory가 상속받게 하면 끝

public interface ProductRepository extends JpaRepository<Product, Long>,ProductRepositoryCustom
profile
웹 벡엔드 개발자가 되어보자!

1개의 댓글

comment-user-thumbnail
2023년 8월 11일

좋은 글 감사합니다. 자주 방문할게요 :)

답글 달기

관련 채용 정보