querydsl
querydsl 문제 해결 - 2023-02-16
jinheung90
2023. 2. 21. 16:21
- 초기 버전의 쿼리
QClickAggregate qClickAggregate = QClickAggregate.clickAggregate;
QProduct qProduct = QProduct.product;
QBrand qBrand = QBrand.brand;
QProductCategory qProductCategory = QProductCategory.productCategory;
QCategory qCategory = QCategory.category;
JPAQuery<ProductFittingClickHistoryDTO> query =
jpaQueryFactory.select(new QProductClickCountDTO(
qProduct.id,
qProduct.thumbnailImageUrl,
qProduct.nameEn,
qProduct.nameKo,
qProduct.productNo,
qClickAggregate.clickCount.sum().longValue(),
qBrand.name
))
.from(qProduct)
.join(qClickAggregate)
.on(qClickAggregate.productId.eq(qProduct.id))
.innerJoin(qProduct.productCategories, qProductCategory)
.innerJoin(qProductCategory.category, qCategory)
.innerJoin(qProduct.brand, qBrand)
.where(qClickAggregate.aggregateDate.between(startDate, endDate));
(기타 검색 조건문, 쿼리 생략)
return QueryDslUtil.getPage(query.groupBy(qProduct.id), PageRequest.of(page, size));
}
- 날짜 기간으로 제품을 클릭한 클릭 수를 더해서 정렬한 다음 제품의 정보를 리스트로 보여주는 쿼리였습니다 (검색 조건 카테고리, 상품명 등등)
- 전체 카테고리도 검색 가능해야 합니다
- 문제
- 합계를 더할 때 특정 제품의 특정 날짜 클릭 수가 2배 또는 3배로 더해지던 문제
- 문제 지점
- 카테고리가 제품이랑 중간테이블을 낀 1 : n, n : 1의 관계였기 때문에 카테고리가 여러개면 검색이 중복해서 일어나서 더해져서 제품이 가지고 있는 카테고리 개수만큼 클릭수가 더해진 상황
- 로직 실행 순서
- 카테고리로 제품을 검색합니다
- 해당 제품들의 아이디를 뽑아 where in 절을 추가 후 집계를 냅니다
- 만약 검색조건이 전체 카테고리면 productId가 가지고 있는 카테고리 목록을 다 뽑습니다
- 카테고리 목록에 있는 카테고리를 DTO에 넣습니다
- 고친 쿼리
- 실행 순서 1번 : 카테고리로 제품을 검색합니다
public List<Long> getProductIdsByCategoryId(Long categoryId) {
QProduct qProduct = QProduct.product;
QProductCategory qProductCategory = QProductCategory.productCategory;
QCategory qCategory = QCategory.category;
return jpaQueryFactory.select(new QIdDTO(qProduct.id))
.from(qProduct)
.innerJoin(qProduct.productCategories, qProductCategory)
.innerJoin(qProductCategory.category, qCategory)
.where(qCategory.id.eq(categoryId))
.fetch().stream().map(IdDTO::getId).collect(Collectors.toList());
}
- 실행 순서 2, 3번 : 해당 제품들의 아이디를 뽑아 where in 절을 추가 후 집계를 냅니다
QClickAggregate qClickAggregate = QClickAggregate.clickAggregate;
QProduct qProduct = QProduct.product;
QBrand qBrand = QBrand.brand;
JPAQuery<ProductFittingClickHistoryDTO> query =
jpaQueryFactory.select(new QProductClickCountDTO(
qProduct.id,
qProduct.thumbnailImageUrl,
qProduct.nameEn,
qProduct.nameKo,
qProduct.productNo,
qClickAggregate .clickCount.sum().longValue(),
qBrand.name
))
.from(qProduct)
.join(qBatchProductFittingHistory)
.on(qClickAggregate .productId.eq(qProduct.id))
.innerJoin(qProduct.brand, qBrand)
.where(qClickAggregate .aggregateDate.between(startDate, endDate));
**if(categoryId != null && categoryId > 0L) {
List<Long> productIds = this.getProductIdsByCategoryId(categoryId);
query = query.where(qProduct.id.in(productIds));
}**
return QueryDslUtil.getPage(query.groupBy(qProduct.id), PageRequest.of(page, size));
- 실행 순서 4 : 만약 검색조건이 전체 카테고리면 productId가 가지고 있는 카테고리 목록을 다 뽑습니다
QProduct qProduct = QProduct.product;
QProductCategory qProductCategory = QProductCategory.productCategory;
QCategory qCategory = QCategory.category;
return jpaQueryFactory.select(new QIdNameDTO(qProduct.id, qCategory.name))
.distinct()
.from(qProduct)
.innerJoin(qProduct.productCategories, qProductCategory)
.innerJoin(qProductCategory.category, qCategory)
.where(qProduct.id.in(productIds))
.fetch();
- 실행 순서 5 : 카테고리를 DTO에 넣습니다
List<IdNameDTO> categoryNameAndProductIds =
productDetailClickAggregateQuery.getCategoryIdNamesByProductIds(productIds);
Map<Long, ProductFittingClickHistoryDTO> map = result
.getContent()
.stream()
.collect(Collectors.toMap(ProductClickCountDTO::getProductId, a -> a));
for (IdNameDTO idNameDTO: categoryNameAndProductIds
) {
ProductFittingClickHistoryDTO productFittingClickHistoryDTO =
map.get(idNameDTO.getId());
productFittingClickHistoryDTO.addCategories(idNameDTO.getName());
}
- querydsl을 쓰면서 고려했던 점들
- fetch join + page를 쓰는 것을 피했습니다
- fetch join을 안하고 연결 객체를 가져다가 쓰는 것을 피했습니다 (n + 1)