[JPA2] API개발과 고급-페치조인최적화와 페이징 한계돌파(3)

레몬커드요거트·2022년 11월 9일
0

V3: 페치 조인 최적화

@GetMapping("/api/v3/orders")
public List<OrderDto> ordersV3(){
  List<Order> orders = orderRepository.finAllWithItem();

  for(Order order : orders){
    System.out.println("order ref= " + order + " id=" + order.getId());
  }

  List<OrderDto> result = orders.stream()
    .map(o -> new OrderDto(o))
    .collect(Collectors.toList());
  return result;
}
public List<Order> finAllWithItem() {
  return em.createQuery(
      "select distinct o from Order o" +
      "join fetch o.member m" +
      "join fetch o.delivery d" +
      "join fetch o.orderItems oi"+
      "join fetch oi.item i", Order.class)
    .getResultList();
}

테이블 4개가 다 조인된 상태, DB가 N만큼 증가 > 쿼리는 1번 나옴
distinct 사용하여 중복을 조회 됨을 막아줌

단, 페이징 불가능

[
    {
        "orderId": 4,
        "name": "userA",
        "orderDate": "2022-11-09T10:35:02.844522",
        "orderStatus": "ORDER",
        "address": {
            "city": "서울",
            "street": "1",
            "zipcode": "1111"
        },
        "orderItems": [
            {
                "itemName": "JPA1 BOOK",
                "orderPrice": 10000,
                "count": 1
            },
            {
                "itemName": "JPA2 BOOK",
                "orderPrice": 20000,
                "count": 2
            }
        ]
    },
    {
        "orderId": 11,
        "name": "userB",
        "orderDate": "2022-11-09T10:35:02.92881",
        "orderStatus": "ORDER",
        "address": {
            "city": "진주",
            "street": "2",
            "zipcode": "2222"
        },
        "orderItems": [
            {
                "itemName": "SPRING1 BOOK",
                "orderPrice": 20000,
                "count": 3
            },
            {
                "itemName": "SPRING2 BOOK",
                "orderPrice": 40000,
                "count": 4
            }
        ]
    }
]

V3.1:엔티티를 DTO로 변환 - 페이징 한계 돌파

public List<Order> findAllWithMemberDelivery(int offset, int limit) {
  return em.createQuery(
    "select o from Order o" +
    " join fetch o.member m" +
    " join fetch o.delivery d", Order.class)
    .setFirstResult(offset)
    .setMaxResults(limit)
    .getResultList();
}
@GetMapping("/api/v3.1/orders")
public List<OrderDto> ordersV3_page(
  @RequestParam(value = "offset", defaultValue="0") int offset,
  @RequestParam(value = "limit", defaultValue = "100") int limit
)
{
  List<Order> orders = orderRepository.findAllWithMemberDelivery(offset, limit);
  List<OrderDto> result = orders.stream()
    .map(o -> new OrderDto(o))
    .collect(Collectors.toList());
  return result;
}
spring:
 	jpa:
 		properties:
 			hibernate:
 				default_batch_fetch_size: 1000

개별로 적용하고 싶을 땐, @BatchSize

  • 장점
    • 쿼리 호출 수가 1 + N 1 + 1 로 최적화 된다.
    • 조인보다 DB 데이터 전송량이 최적화 된다. (Order와 OrderItem을 조인하면 Order가
      OrderItem 만큼 중복해서 조회된다. 이 방법은 각각 조회하므로 전송해야할 중복 데이터가 없다.)
    • 페치 조인 방식과 비교해서 쿼리 호출 수가 약간 증가하지만, DB 데이터 전송량이 감소한
    • 컬렉션 페치 조인은 페이징이 불가능 하지만 이 방법은 페이징이 가능하다.
profile
비요뜨 최고~

0개의 댓글