[241126 TIL]
MSA 학습
MSA 프로젝트 구현
ProductRepositoryImpl 코드 분석
- ProductRepositoryCustom 인터페이스를 구현하여, QueryDSL을 기반으로 복잡한 검색 로직 수행
- 주요 기능- 동적 검색 조건, 정렬, 페이징
코드 전체 구조
@RequiredArgsConstructor
public class ProductRepositoryImpl implements ProductRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public Page<ProductResponseDto> searchProducts(ProductSearchDto searchDto, Pageable pageable) {
// Pageable 객체에서 정렬 조건 추출
List<OrderSpecifier<?>> orders = getAllOrderSpecifiers(pageable);
// QueryDSL로 동적 검색 수행
QueryResults<Product> results = queryFactory
.selectFrom(product)
.where(
nameContains(searchDto.getName()), // 이름 검색 조건
descriptionContains(searchDto.getDescription()), // 설명 검색 조건
priceBetween(searchDto.getMinPrice(), searchDto.getMaxPrice()), // 가격 범위 조건
quantityBetween(searchDto.getMinQuantity(), searchDto.getMaxQuantity()) // 수량 범위 조건
)
.orderBy(orders.toArray(new OrderSpecifier[0])) // 정렬 조건 추가
.offset(pageable.getOffset()) // 시작 위치 설정
.limit(pageable.getPageSize()) // 페이징 크기 설정
.fetchResults(); // 쿼리 실행
// 검색 결과를 DTO로 변환
List<ProductResponseDto> content = results.getResults().stream()
.map(Product::toResponseDto)
.collect(Collectors.toList());
// 총 검색된 데이터 수
long total = results.getTotal();
// Page 객체로 결과 반환
return new PageImpl<>(content, pageable, total);
}
// 이름 포함 조건
private BooleanExpression nameContains(String name) {
return name != null ? product.name.containsIgnoreCase(name) : null;
}
// 설명 포함 조건
private BooleanExpression descriptionContains(String description) {
return description != null ? product.description.containsIgnoreCase(description) : null;
}
// 가격 범위 조건
private BooleanExpression priceBetween(Double minPrice, Double maxPrice) {
if (minPrice != null && maxPrice != null) {
return product.price.between(minPrice, maxPrice);
} else if (minPrice != null) {
return product.price.goe(minPrice);
} else if (maxPrice != null) {
return product.price.loe(maxPrice);
} else {
return null;
}
}
// 수량 범위 조건
private BooleanExpression quantityBetween(Integer minQuantity, Integer maxQuantity) {
if (minQuantity != null && maxQuantity != null) {
return product.quantity.between(minQuantity, maxQuantity);
} else if (minQuantity != null) {
return product.quantity.goe(minQuantity);
} else if (maxQuantity != null) {
return product.quantity.loe(maxQuantity);
} else {
return null;
}
}
// 정렬 조건 생성
private List<OrderSpecifier<?>> getAllOrderSpecifiers(Pageable pageable) {
List<OrderSpecifier<?>> orders = new ArrayList<>();
// Pageable의 정렬 조건을 QueryDSL의 OrderSpecifier로 변환
if (pageable.getSort() != null) {
for (Sort.Order sortOrder : pageable.getSort()) {
// 정렬 방향 결정 (ASC/DESC)
com.querydsl.core.types.Order direction = sortOrder.isAscending()
? com.querydsl.core.types.Order.ASC
: com.querydsl.core.types.Order.DESC;
// 정렬 필드에 따른 OrderSpecifier 추가
switch (sortOrder.getProperty()) {
case "createdAt":
orders.add(new OrderSpecifier<>(direction, product.createdAt));
break;
case "price":
orders.add(new OrderSpecifier<>(direction, product.price));
break;
case "quantity":
orders.add(new OrderSpecifier<>(direction, product.quantity));
break;
default:
break;
}
}
}
return orders;
}
}
searchProducts 메서드
1. 페이징 및 정렬 처리
List<OrderSpecifier<?>> orders = getAllOrderSpecifiers(pageable);
- getAllOrderSpecifiers(pageable):
- QueryDSL의 정렬 객체(OrderSpecifier)를 생성하는 메서드
- Pageable의 정렬 조건에 따라 QueryDSL에서 사용할 정렬 정보를 동적으로 생성
private List<OrderSpecifier<?>> getAllOrderSpecifiers(Pageable pageable) {
List<OrderSpecifier<?>> orders = new ArrayList<>();
if (pageable.getSort() != null) {
for (Sort.Order sortOrder : pageable.getSort()) {
com.querydsl.core.types.Order direction = sortOrder.isAscending()
? com.querydsl.core.types.Order.ASC
: com.querydsl.core.types.Order.DESC;
switch (sortOrder.getProperty()) {
case "createdAt":
orders.add(new OrderSpecifier<>(direction, product.createdAt));
break;
case "price":
orders.add(new OrderSpecifier<>(direction, product.price));
break;
case "quantity":
orders.add(new OrderSpecifier<>(direction, product.quantity));
break;
default:
break;
}
}
}
return orders;
}
- Pageable 객체의 Sort 정보를 기반으로 QueryDSL의 OrderSpecifier 생성
- 정렬 속성(property)에 따라 createdAt, price, quantity 필드의 정렬 조건 추가
2. QueryDSL 검색 쿼리
QueryResults<Product> results = queryFactory
.selectFrom(product)
.where(
nameContains(searchDto.getName()),
descriptionContains(searchDto.getDescription()),
priceBetween(searchDto.getMinPrice(), searchDto.getMaxPrice()),
quantityBetween(searchDto.getMinQuantity(), searchDto.getMaxQuantity())
)
.orderBy(orders.toArray(new OrderSpecifier[0]))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetchResults();
- queryFactory.selectFrom(product)
- QueryDSL로 Product 엔터티를 검색
- where(...)
- 검색 조건을 동적으로 추가
- 각 조건은 BooleanExpression 객체로 반환되며, null인 경우 무시
- orderBy(orders.toArray(...))
- 정렬 조건 적용
- offset(pageable.getOffset()) & limit(pageable.getPageSize())
- 페이징 처리: 요청한 페이지의 시작 위치(offset)와 크기(limit)를 설정
3. 결과 처리
List<ProductResponseDto> content = results.getResults().stream()
.map(Product::toResponseDto)
.collect(Collectors.toList());
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
- getResults(): 조회된 Product 객체 리스트.
- toResponseDto(): 각 Product 엔터티를 ProductResponseDto로 변환.
- getTotal(): 전체 검색 결과 수.
- PageImpl:
- Spring Data의 Page 구현체로, 결과 데이터(content), 페이징 정보(pageable), 총 데이터 수(total)를 포함.
'[내일배움캠프] AI 를 활용한 백엔드 아카데미 심화 과정 > TIL' 카테고리의 다른 글
Redis 명령어 (2) | 2024.11.28 |
---|---|
Docker (0) | 2024.11.27 |
MSA 프로젝트 (0) | 2024.11.25 |
MSA(보안구성, Config, 분산추적, 이벤트 드리븐) (1) | 2024.11.22 |
MSA(서비스 디스커버리, 로드 밸런싱, 서킷 브레이커, API GW) (2) | 2024.11.21 |