QueryDsl에서 상품을 조회 할 때, 상품의 개수만큼 쿼리문이 동작하고 그거에 따라 카테고리 조회문도 상품의 개수만큼 다시 도는 문제가 발생했습니다. 한마디로 20개의 상품을 조회하면 60회의 쿼리문이 동작했습니다.
1. 문제의 queryDSL 쿼리문
@Override
public List<Product> findByCategoryIds(Long categoryId) {
QProduct product = QProduct.product;
QBookCategory bookCategory = QBookCategory.bookCategory;
QCategory category = QCategory.category;
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
// 카테고리 계층 쿼리 (기존 쿼리와 동일)
BooleanExpression isChildCategory = category.id.eq(categoryId);
isChildCategory = isChildCategory.or(category.parent.id.eq(categoryId));
return queryFactory
.selectFrom(product)
.leftJoin(product.bookCategories, bookCategory).fetchjoin()
.leftJoin(bookCategory.category, category).fetchjoin()
.where(isChildCategory)
.distinct()
.fetch();
}
fetchJoin될 것을 기대 했지만, 밑의 상황을 보면 fetchJoin으로 동작하지 않는다....
1 - 1. 상황

좀 자세하게 살펴보니 product_img_detail 테이블에서 아이디 별로 뭔가 즉시 로딩으로 불러오는 것이 확인 됐습니다.
Product와 ProducImgDetail을 살펴봅시다!!.
Product Entity 부분
@OneToOne(mappedBy = "product", fetch = FetchType.LAZY)
@JsonIgnoreProperties("product")
@ToString.Exclude
private ProductImgDetail productImgDetail;
ProductImgDetail 부분
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
@ToString.Exclude
private Product product;
2. 여러 해결방법 중 드디어 원인을 찾았습니다.
OneToOne Fetch Lazy 적용이 안되는 이유
이전에 영한님 강의에서 본 기억이 어렴풋이 기억났습니다. OneToOne 관계에서 연관관계 주인이 아닌 쪽은 즉시 로딩으로 동작한다는 것이 문제였습니다.
3. 해결
Product Entity 부분
@OneToOne(mappedBy = "product")
@JsonIgnoreProperties("product")
@ToString.Exclude
private ProductImgDetail productImgDetail;
QueryDsl 조회문 부분
@Override
public List<Product> findByCategoryIds(Long categoryId) {
QProduct product = QProduct.product;
QProductImgDetail productImgDetail = QProductImgDetail.productImgDetail;
QBookCategory bookCategory = QBookCategory.bookCategory;
QCategory category = QCategory.category;
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
// 카테고리 계층 쿼리 (기존 쿼리와 동일)
BooleanExpression isChildCategory = category.id.eq(categoryId);
isChildCategory = isChildCategory.or(category.parent.id.eq(categoryId));
JPAQuery<Product> productJPAQuery = queryFactory
.selectFrom(product)
.leftJoin(product.bookCategories, bookCategory)
.leftJoin(bookCategory.category, category)
.leftJoin(product.productImgDetail, productImgDetail).fetchJoin()
.where(isChildCategory)
.distinct();
return productJPAQuery.fetch();
}
productImgDetail을 fetchJoin()하는 동작을 추가하여 결과적으로 아래와 같은 쿼리문으로 1번 동작으로 변경 됐습니다. JPA로 개발하면 할수록 이런 쿼리문 최적화가 정말 재밌다고 느껴집니다.
