지금까지는 xxxtoOne(OneToOne, ManyToOne)
관계만 있었는데,
이번에는 컬렉션인 일대다 관계(OneToMany
)를 조회하고 최적화하는 방법을 알아보자!
OrderApiController
@RestController
@RequiredArgsConstructor
public class OrderApiController {
private final OrderRepository orderRepository;
/**
* V1. 엔티티 직접 노출
* - Hibernate5Module 모듈 등록, LAZY=null 처리
* - 양방향 관계 문제 발생 -> @JsonIgnore
*/
@GetMapping("/api/v1/orders")
public List<Order> ordersV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
for (Order order : all) {
order.getMember().getName(); //Lazy 강제 초기화
order.getDelivery().getAddress(); //Lazy 강제 초기화
List<OrderItem> orderItems = order.getOrderItems();
orderItems.stream().forEach(o -> o.getItem().getName()); //Lazy 강제 초기화
}
return all;
}
}
orderItem
, item
관계를 직접 초기화하면 Hibernate5Module
설정에 의해 엔티티를 JSON으로 생성한다.@JsonIgnore
를 추가해야 한다.OrderApiController
@RestController
@RequiredArgsConstructor
public class OrderApiController {
private final OrderRepository orderRepository;
...
/**
* V2. 엔티티를 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(toList());
return collect;
}
@Getter
static class OrderDto {
private Long orderId;
private String name;
private LocalDateTime orderDate; //주문시간
private OrderStatus orderStatus;
private Address address;
private List<OrderItemDto> orderItems; //Dto 변환 필수!!
public OrderDto(Order order) {
orderId = order.getId();
name = order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress();
orderItems = order.getOrderItems().stream()
.map(orderItem -> new OrderItemDto(orderItem))
.collect(toList());
}
}
@Getter
static class OrderItemDto {
private String itemName; //상품명
private int orderPrice; //주문 가격
private int count; //주문 수량
public OrderItemDto(OrderItem orderItem) {
itemName = orderItem.getItem().getName();
orderPrice = orderItem.getOrderPrice();
count = orderItem.getCount();
}
}
}
order
1번member
, address
N번 (order
조회 수 만큼)orderItem
N번 (order
조회 수 만큼)item
N번 (orderItem
조회 수 만큼)🚫 주의
Dto 안에는 엔티티가 있으면 안된다.
OrderDto
안에 있는orderItems
조차도 Dto로 변환해주어야 한다.
OrderApiController
@GetMapping("/api/v3/orders")
public List<OrderDto> ordersV3() {
List<Order> orders = orderRepository.findAllWithItem();
List<OrderDto> result = orders.stream()
.map(o -> new OrderDto(o))
.collect(toList());
return result;
}
OrderRepository
public List<Order> findAllWithItem() {
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();
}
distinct
를 사용하는 이유?order
엔티티의 조회 수도 증가하게 된다.disctinct
는 SQL에 distinct
를 추가하고, 더해서 같은 엔티티가 조회되면, 애플리케이션에서 중복을 걸러준다. 즉, order
가 조인 때문에 중복 조회되는 것을 막아준다.