g-market / b-shop-backend

0 stars 0 forks source link

refactor: where 다중 컬럼 in 절 비교를 통해 성능 개선 #31

Closed halucinor closed 1 year ago

halucinor commented 1 year ago

In GitLab by @haaeee on Feb 21, 2023, 10:00


name: Refactor Template assignees: 'jaime'

현재 장바구니는 Redis로 구현하였습니다.

Redis의 자료구조는 Hashes를 사용하여, key는 memberId, hashKey는 itemId+itemOptionId hashValue는 CartDto입니다.

public record CartDto(
    Long itemId,
    Long itemOptionId,
    int orderCount
) {
}

CartDto에 있는 itemId와 itemOptionId 값을 통해 itemOption Table에서

itemTable과 category 테이블, itemImage 테이블을 조인하여 값을 내려줘야 합니다.

첫 번째 방법

이를 조회하기 위해 CartDto에 담긴 itemId, itemOptionId를 하나씩 가져와

itemId, itemOptionId에 해당하는 쿼리를 사용하여 itemOpion Entity를 조회하면 됩니다.

다만 이 때 장바구니에 100개의 상품이 담겨 있다면 100번의 쿼리가 발생합니다.

두번 째 방법

List 에서 itemId, itemOptionId를 각각의 Id list을 만든 후,

item과 연관된 itemIdList에 존재하고, itemOptionId itemOptionIdList에 존재하는

ItemOption 엔티티를 모두 조회하는 방법입니다.

이는 애플리케이션에서 다시 올바르게 조회되었는지 검증 로직이 필요합니다.

public List<ItemOption> findWithItemAndCategory(Collection<Long> itemIdList, Collection<Long> itemOptionIdList) {
        return jpaQueryFactory.select(itemOption)
            .from(itemOption)
            .join(itemOption.item, item).fetchJoin()
            .join(item.category, category).fetchJoin()
            .join(item.image, itemImage).fetchJoin()
            .where(item.id.in(itemIdList).and(itemOption.id.in(itemOptionIdList)))
            .orderBy(item.id.asc(), itemOption.id.asc())
            .fetch();
    }

이 또한 원하는 쿼리의 방향과 맞지 않습니다. 현재 원하는 것은 다음 아래의 쿼리를 찾고 싶기 때문입니다.

원하는 쿼리

select item정보
    from
        item_option io
    join
        item i
            on i.id=io.item_id 
    join
        category c
            on c.id=i.category_id 
    join
        ItemImage ii
            on ii.id=i.item_image_id
    where
        (
            io.item_id,io.id
        ) in( (itemId1,itemOptionId1), (itemId2,itemOptionId2), (itemId3,itemOptionId3) )

세번 째 방법

public List<ItemOption> findWithItemAndCategoryAndImageByItemIdListAndIdList(List<CartDto> cartDtoList) {
        return jpaQueryFactory.select(itemOption)
            .from(itemOption)
            .join(itemOption.item, item).fetchJoin()
            .join(item.category, category).fetchJoin()
                        .join(item.image, itemImage).fetchJoin()
            .where(Expressions.list(item.id, itemOption.id).in(searchItemIdAndItemOptionIdIn(cartDtoList)))
            .fetch();
    }

private Expression[] searchItemIdAndItemOptionIdIn(List<CartDto> cartDtoList) {

    List<Expression<Object>> tuples = new ArrayList<>();

    for (CartDto cartDto: cartDtoList) {
        tuples.add(Expressions.template(Object.class, "(({0}, {1}))", cartDto.itemId(),
            cartDto.itemOptionId()));
    }
    return tuples.toArray(new Expression[0]);
    }
    select
        i1_0.id,
        i1_0.created_at,
        i1_0.description,
        i2_0.id,
        i2_0.base_price,
        c1_0.id,
        c1_0.name,
        i2_0.created_at,
        i2_0.deleted,
        i2_0.description,
        i2_0.item_status,
        i2_0.name,
        i2_0.open_at,
        i2_0.updated_at,
        i1_0.option_level,
        i1_0.option_price,
        i1_0.stock_quantity,
        i1_0.updated_at 
    from
        item_option i1_0 
    join
        item i2_0 
            on i2_0.id=i1_0.item_id 
    join
        category c1_0 
            on c1_0.id=i2_0.category_id 
    where
        (
            i1_0.item_id,i1_0.id
        ) in((?,?),(?,?),(?,?))

📄 참고 사항

⏰ 예상 소요 기간