<aside> ❗
@GetMapping("/api/v1/orders")
public List<Order> orderV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
for (Order order : all) {
order.getMember().getName();
order.getDelivery().getAddress();
List<OrderItem> orderOrderItems = order.getOrderItems();
for (OrderItem orderItem : orderOrderItems) {
orderItem.getItem().getName();
}
}
return all;
}
<aside> ❗
엔티티
를 모두 DTO로 바꿔야 한다.@GetMapping("/api/v2/orders")
public List<OrderDto> orderV2() {
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
List<OrderDto> collect = orders.stream().map(o -> new OrderDto(o)).collect(Collectors.toList());
return collect;
}
@Data
static class OrderDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
private List<OrderItemDto> orderItems;
public OrderDto(Order order) {
this.orderId = order.getId();
this.name = order.getMember().getName();
this.orderDate = order.getOrderDate();
this.orderStatus = order.getOrderStatus();
this.address = order.getDelivery().getAddress();
this.orderItems = order.getOrderItems().stream()
.map(orderItem -> new OrderItemDto(orderItem))
.collect(Collectors.toList());
}
}
@Data
static class OrderItemDto {
private String itemName;
private int orderPrice;
private int count;
public OrderItemDto(OrderItem orderItem) {
this.itemName = orderItem.getItem().getName();
this.orderPrice = orderItem.getItem().getPrice();
this.count = orderItem.getCount();
}
}
[참고]
<aside> ❗
단점 : 페이징 불가능
→ 데이터가 어떻게 뻥튀기 될지 모르기 때문에 고정된 단위로 페이징 할 수 없음
→ 모든 데이터를 메모리에 읽어와 메모리에서 페이징을 수행
→ 데이터가 많은 경우 문제 발생 !!!!
(Out Of Memory 발생 가능)public List<Order> findAllWitehItem() {
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", Order.class)
.getResultList();
}
// v2와 동일
@GetMapping("/api/v3/orders")
public List<OrderDto> orderV3() {
List<Order> orders = orderRepository.findAllWitehItem();
List<OrderDto> collect = orders.stream().map(o -> new OrderDto(o)).collect(Collectors.toList());
return collect;
}
[참고]
컬렉션 페치 조인은 1개만 사용
**할 수 있다.<aside> ❗
컬렉션을 페치 조인하면 페이징 불가능
일대다 조인이 발생하므로 데이터가 예측할 수 없이 증가
**한다.xToOne(OneToOne, ManyToOne)
관계를 모두 페치모인 한다.
Order → Member, Delivery
A → B → C 도 가능 (A, B, C 모두 ToOne 인 경우도 가능, B는 C를 의존)컬렉션은 지연 로딩으로 조회
**한다. (페치 조인 하지 마라)
→ 페이징 가능해짐hibername.default_batch_fetch_size,
- 글로벌 설정
@BatchSize
를 적용한다.
→ N + 1 의 경우 하나씩 Select를 수행하지만 이를 사용하면 정해진 개수 만큼 미리 가져온다.
) jpa:
properties:
hibernate:
default_batch_fetch_size:
ToMany는 해당 필드에 @BatchSize() 애너테이션을 붙이고
@BatchSize(size = 1000)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
ToOne인 경우 해당 엔티티 상단에 @BatchSize() 애너테이션을 붙여야 한다.
@BatchSize(size = 1000)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item { ... }
[처리 과정]
select oi1_0.order_id,oi1_0.order_item_id,oi1_0.count,oi1_0.item_id,oi1_0.order_price
from order_item oi1_0
where oi1_0.order_id in (1,2)
select i1_0.item_id,i1_0.dtype,i1_0.name,i1_0.price,i1_0.stock_quantity,i1_0.artist,i1_0.etc,i1_0.author,i1_0.isbn,i1_0.actor,i1_0.director
from item i1_0
where i1_0.item_id in (1,2,3,4)
[참고]
쿼리는 더 나가지만 중복 데이터가 없음
DB에서 애플리케이션으로 전송되는 데이터의 크기가 작다.</aside>
<aside> ❗
spring:
jpa:
properties:
hibernate:
default_batch_fetch_size: 1000
@BatchSize
를 적용하면 된다.
(컬렉션은 컬렉션 필드에, 엔티티는 클래스에 적용)[장점]
조인보다 DB 데이터 전송량이 최적화
된다. - 대부분은 페치 조인을 사용할 것
Order와 OrderItem을 조인하면 Order가 OrderItems 만큼 중복해서 조회된다.
하지만 현재 방법은 각각 조회하므로 전송해야할 중복 데이터가 없다.
컬렉션 페치 조인은 페이징이 불가능
하지만 **이 방법은 페이징이 가능
**하다.ToOne 관계는 페치 조인으로 쿼리 수를 줄이고
나머지는 hibernate.default_batch_fetch_size로 최적화하자
[참고]
의 크기는 적당한 사이즈를 골라야 하는데, **
100~1000 사이를 선택하는것을 권장`**한다.DB든 애플리케이션이든 순간 부하를 어디까지 견딜 수 있는지로 결정
**하면 된다.</aside>
<aside> ❗
</aside>
<aside> ❗
</aside>
<aside> ❗
</aside>
<aside> ❗
</aside>