JPA/JPA 활용
컬렉션 조회 최적화 1 - 페치 조인을 안 했을 시
chanhee01
2023. 8. 10. 15:57
@OneToMany에서의 최적화를 하는 방법은 엔티티 조회 최적화보다 조금 더 까다롭다.
여기서도 이전 방식처럼 성능이 안 나오는 것부터 점진적으로 발전해나가면서 할 예정이다.
1. 엔티티를 그대로 노출
@GetMapping("/api/v1/orders")
public List<Order> ordersV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
for (Order order : all) {
order.getMember().getName();
order.getDelivery().getAddress();
List<OrderItem> orderItems = order.getOrderItems();
orderItems.stream().forEach(o -> o.getItem().getName());
}
return all;
}
API를 만들 때 엔티티를 직접 노출하는 것은 좋지 않다고 여러번 얘기했다. 엔티티를 직접 노출하는 것은 피해야 한다.
2. DTO로 변환해서 반환하기
@GetMapping("/api/v2/orders")
public List<OrderDto> ordersV2() {
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
List<OrderDto> collect = orders.stream()
.map(o -> new OrderDto(o))
.collect(Collectors.toList());
return collect;
}
OrderDto를 스트림으로 돌릴 때 Order 목록을 루프로 돌리고 Order에서의 orderItems 목록을 또 루프로 돌린다. 그런데 이 때에는 SQL이 너무 많이 나온다는 단점이 있다. Order를 조회하는 SQL에서 order가 2번 조회된다. 그 이후에 member, delivery의 쿼리가 나가고 orderItems에서 item이 2개가 있기 때문에 item을 찾는 쿼리가 2번 나간다. 이 과정이 첫 번째 order의 SQL 호출 과정이고 다음 주문에서도 쿼리가 이만큼 나간다.
쿼리가 너무 많이 나가기 때문에 성능상 문제가 되고 최적화를 더 신경써야만 한다.
OrderDto
@Getter
static class OrderDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
private List<OrderItem> orderItems; // 여기에서 OrderItem이 들어간게 문제
public OrderDto(Order order) {
orderId = order.getId();
name = order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress();
orderItems = order.getOrderItems();
}
}
Dto를 만들 때에 주의점은 Dto에서도 엔티티를 사용하면 안된다는 것이다.
별도의 OrderItemDto를 만들어서 위에 있는 OrderItem도 OrderItemDto로 변환해줘야 한다.
@Getter
static class OrderDto {
....
private List<OrderItemDto> orderItems; // OrderItemDto로 보내기
public OrderDto(Order order) {
....
orderItems = order.getOrderItems().stream()
.map(orderItem -> new OrderItemDto(orderItem))
.collect(Collectors.toList()); // 여기도 Dto로 변환 필요
}
}
@Getter
static class OrderItemDto {
private String itemName;
private int orderPrice;
private int count;
public OrderItemDto(OrderItem orderItem) {
itemName = orderItem.getItem().getName();
orderPrice = orderItem.getItem().getPrice();
count = orderItem.getCount();
}
}
Dto를 만들었어도 쿼리가 너무 많이 나가기 때문에 저번에 공부했던 fetch join을 이용해서 최적화를 진행해야 한다.