개발일지/Spring

Spring-boot Querydsl 사용해보기!

2023. 1. 23. 23:00
목차
  1. build.gradle
  2. QuerydslConfig
  3. ProductRepositoryCustom
  4. ProductRepositoryCustomImpl
  5. ProductRepository
728x90

JPA를 이용해 간편하게 메서드 네이밍을 사용하던 중 문제가 발생했다

 

A and (B or C)를 찾는 메서드를 만들어야 하는데 어렵다...

 

사실 그냥 쿼리를 만들면 쉽긴 하다만JPA메서드네이밍만으로 할 수 있을 것 같아서 도전해 봤다

 

Page<Product> findAllByProductCategories_Category_CategoryIdAndNameContainsOrProductCategories_Category_CategoryIdAndBrandContains(long categoryId1, String name, long categoryId2, String brand, Pageable pageable);

 

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

만들고 보니 어이가 없다 ㅋㅋㅋ

 

그래도 약간의 설명을 해보자면

특정 카테고리에 속한 제품 중 제품명이나 브랜드를 이용하여 검색하는 메서드다

 

A and (B or C)를 해야 하는데 네이밍 컨벤션으로는 해당 방법이 불가능해서

(A and B) or (A and C) 방법으로 만들어봤다

 

뭐... 결과는 똑같이 나온다만...ㅎ

 

가독성이 굉장히 떨어지며 

어떤 쿼리가 나갈지 전혀 예측이 안되므로 유지보수 측면에서 상당히 좋지 않다.

 

그래서 Querydsl을 사용해보려 한다

 

build.gradle

아래와 같이 추가해 준다.

buildscript { // Spring Boot 2.6 이상일 경우
    ext {
        queryDslVersion = "5.0.0"
    }
}

dependencies {
    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
    implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
}

def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main.java.srcDir querydslDir
}
compileQuerydsl{
    options.annotationProcessorPath = configurations.querydsl
}
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    querydsl.extendsFrom compileClasspath
}

 

QuerydslConfig

해당 클래스를 생성해 주면 공통으로 사용할 수 있다.

@Configuration
public class QuerydslConfig {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

ProductRepositoryCustom

추상 메서드를 정의한다.

public interface ProductRepositoryCustom {
    Page<Product> searchAll(Long categoryId, String keyword, Pageable pageable);
}

 

ProductRepositoryCustomImpl

그리고 구현!

@RequiredArgsConstructor
public class ProductRepositoryCustomImpl implements ProductRepositoryCustom {
    private final JPAQueryFactory queryFactory;

    @Override
    public Page<Product> searchAll(Long categoryId, String keyword, Pageable pageable) {
        JPAQuery<Product> query = queryFactory
                .selectFrom(product)
                .where(
                        eqCategory(categoryId),
                        eqKeyword(keyword)
                );

        List<Product> result = query
                .orderBy(getOrderSpecifier(pageable))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        return PageableExecutionUtils.getPage(result, pageable, query::fetchCount);
    }

    private BooleanExpression eqCategory(Long categoryId) {
        return categoryId == null
                ? null
                : product.productCategories.any().category.categoryId.eq(categoryId);
    }

    private BooleanExpression eqKeyword(String keyword) {
        return keyword == null
                ? null
                : product.name.contains(keyword)
                .or(product.description.contains(keyword))
                .or(product.brand.contains(keyword));
    }

    private OrderSpecifier getOrderSpecifier(Pageable pageable) {
        Sort.Order direction = pageable.getSort().get().collect(Collectors.toList()).get(0);

        Order order = direction.getDirection().isAscending()
                ? Order.ASC
                : Order.DESC;

        switch (direction.getProperty()) {
            case "name":
                return new OrderSpecifier(order, product.name);
            case "price":
                return new OrderSpecifier(order, product.price);
            case "brand":
                return new OrderSpecifier(order, product.brand);
            case "likeCount":
                return new OrderSpecifier(order, product.likeCount);
            case "soldCount":
                return new OrderSpecifier(order, product.soldCount);
            default:
                return new OrderSpecifier(order, product.productId);
        }
    }
}

 

ProductRepository

그리고 JpaRepository와 ProductRepositoryCustom을 상송받는 ProductRepositroy를 만든다

public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom {
}

 

그리고 ProductRepositroy를 사용하면 된다!

 

장점

  • 생각보다 사용이 굉장히 쉽다
  • 어떤 쿼리가 나갈지 코드만 보아도 대략적인 예측이 가능하여 유지보수 측면에서 좋을것 같다
  • 복잡한 동적쿼리 처리가 용이하다

단점

  • 솔직히 잘 모르겠다. 굳이 꼽으라면 추가적인 학습이 필요하고 jpa 네이밍메서드보다 만들기 귀찮다 ...????

 

두가지 방법을 적절히 섞어 활용 한다면 굉장히 좋을 것 같다.

728x90
저작자표시 비영리 변경금지 (새창열림)

'개발일지 > Spring' 카테고리의 다른 글

싱글톤 패턴 (Singleton Pattern)  (0) 2023.03.03
좋은 객체 지향 설계의 5가지 원칙 (SOLID)  (0) 2023.02.25
캐시 사용해보기 @Cacheable, @CacheEvict (Spring-boot)  (0) 2023.01.13
Spring boot Jwt 토큰의 정보를 가져오는 방법  (0) 2023.01.01
springboot 개발환경에 따른 application.yml 설정  (2) 2022.11.28
  1. build.gradle
  2. QuerydslConfig
  3. ProductRepositoryCustom
  4. ProductRepositoryCustomImpl
  5. ProductRepository
'개발일지/Spring' 카테고리의 다른 글
  • 싱글톤 패턴 (Singleton Pattern)
  • 좋은 객체 지향 설계의 5가지 원칙 (SOLID)
  • 캐시 사용해보기 @Cacheable, @CacheEvict (Spring-boot)
  • Spring boot Jwt 토큰의 정보를 가져오는 방법
E-room
E-room
나의 성취 기록들
E-room
E-room Achievement Logs
E-room
전체
오늘
어제
  • 분류 전체보기
    • 개발일지
      • 돌픽
      • Spring
      • Algorithm
      • Java
      • Node.js
      • Python
      • DataBase
      • 웹개발
      • JavaScript
      • 컴퓨터지식
      • Django
    • 이것저것
    • 피드백 감사히 받겠습니다

블로그 메뉴

  • 태그
  • Github
  • 돌픽-이상형월드컵

인기 글

최근 글

최근 댓글

태그

  • 조합
  • 자바
  • 백트래킹
  • 파이썬
  • Spring
  • python
  • Java
  • SQL
  • 순열
  • Django
  • search
  • mysql
  • 수열
  • 재귀
  • dp
  • 알고리즘
  • 프로그래밍
  • 백준
  • 스파르타코딩클럽
  • algorithm
  • 생활코딩
  • 자료구조
  • dfs
  • 탐색
  • JPA
  • 완전탐색
  • boot
  • API
  • javascript
  • 다이나믹

공지사항

hELLO · Designed By 정상우.
E-room
Spring-boot Querydsl 사용해보기!
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.