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 |