본문 바로가기
강의 정리/스프링 부트와 JPA활용 2

2. 컬렉션 조회최적화

by 이석준석이 2021. 1. 9.

1. 1:N fetch join의 문제점

 

1:N 에서 join을 하면 N 개의 row 가 나오는 문제점이 있다.

  • orders(1) : order_item(n) 의 연관관계의 경우 조인하면 아래와같이 로우가 4개 생긴다.

위의 조인된 컬럼에서 중복을 제거하고 싶다.. 어떻게?

  • JPA의 distinct 를 사용해서 제거한다.
    • DB에 distinct 키워드 제공
    • 엔티티가 중복인 경우, 중복을 제거해서 컬렉션에 담아주는 기능을 제공한다.
em.createQuery(
        "select distinct o from Order o" +
        " join fetch o.orderItems oi" +
        " join fetch oi.item i", Order.class).getResultList();

단점

  • 1:N fetch join 에서는 페이징이 불가능하다. 
  • 1:N fetch join 은 한개만 사용(1:N:M 불가) 할 수 있다.

2. 페이징, 한계 돌파

 

  1. ToOne 관계는 모두 fetch join 을 한다.
  2. 컬렉션은 fetch join 이 아닌 Lazy Loading 으로 조회한다.
  3. Lazy Loading 을 최적화 하기위해 (Lazy Loading 을 In query로 최적화) 
    • spring.jpa.properties.default_batch_fetch_size (글로벌 설정)
    • @BatchSize (개별 설정)

@BatchSize 적용방법

  • 컬렉션(@XToMany)에 적용하는 경우 (프로퍼티에 적는다.)
@BatchSize(size = 1000)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();

 

  • XToOne 에 적용하는 경우 (엔티티에 직접 적는다.)
@BatchSize(size = 100)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {

장점

  • 쿼리 호출수 1+N => 1+1
  • 컬렉션 fetch join 에서 불가능했던 paging 이 가능해짐

default_batch_fetch_size 는 몇개로?

  • 100 ~ 1000 사이를 선택하자
  • IN query를 사용하는데, DB 에서 IN 파라미터를 제한하기도 하기 때문에 데이터베이스 환경에 대한 확인이 필요
  • 사이즈를 크게하면 순간적인 부하가 DB, 어플리케이션에 증가하기 때문에 이를 고려해야 한다.

3. DTO 조회 컬렉션조회에서 최적화

 

방법

  1. Dto 리스트로 받는 쿼리를 작성 (in 절 사용)
  2. 받은 뒤, Map<Id, List<>> 으로 변환
  3. 루프를 돌면서 set
List<OrderItemQueryDto> orderItems = em.createQuery(
        "select new jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name, oi.orderPrice, oi.count)" +
                " from OrderItem oi" +
                " join oi.item i" +
                " where oi.order.id in :orderIds", OrderItemQueryDto.class)
        .setParameter("orderIds", orderIds)