주문 내역에서 추가로 주문한 상품 정보를 추가로 조회하는 경우
Order기준으로 OrderItem과 Item 이 필요함.
order 엔티티에 OrderItems와 OneToMany 관계이다. - 컬렉션 조회 최적화를 통해서 성능을 최적화 해보자.
엔티티 직접노출의 경우
이전에 간단한 주문조회와 마찬가지로 findAllByString을 통해서 전체 조회를 진행한다.
Lazy로딩 문제가 발생했던 것은 이전 해결법과 마찬가지로 강제초기화를 통해 진행
@GetMapping
public List<Order> ordersV1(){
List<Order> all = orderRespository.findAllByString(new OrderSearch());
//간단한조회와 마찬가지로 Lazy강제초기화 진행
for (Order order : all) {
order.getMember().getName();
order.getDelivery().getAddress();
List<OrderItem> orderItems = order.getOrderItems();
//람다식으로 강제초기화
orderItems.stream().forEach(o->o.getItem().getName());
}
return all;
}
양방향 연관관계일 경우 @JsonIgnore를 통해서 무한루프가 걸리지 않게 한곳에 적어줘야함.
하지만 잊으면 안된다. 엔티티 직접 노출을 해버리면 엔티티 스펙이 바뀌면 이 코드가 제대로 돌아가지 않게 된다.
주문조회 v2 - DTO로 변환해서 넘기자.
orderApiController내부에 orderDto를 생성하고 실행 하게 되면 orderItem은 null로 나오지 않게 된다.
@Data
static class OrderDto{
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
private List<OrderItem> orderItems;
public OrderDto(Order order){
orderId = order.getId();
name = order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getOrderStatus();
address = order.getDelivery().getAddress();
orderItems = order.getOrderItems();
}
}
{
"data": [
{
"id": 1,
"member": {
"id": 1,
"name": "userA",
"address": {
"city": "서울",
"street": "1",
"zipcode": "1111"
}
},
"orderItems": null,
"delivery": {
"id": 1,
"address": {
"city": "서울",
"street": "1",
"zipcode": "1111"
},
"status": null
},
"orderDate": "2023-09-14T17:05:27.974346",
"orderStatus": "ORDER",
"totalPrice": 50000
}
]
}
Why? orderItem은 entity기 때문에 null로 나오는 것이다.
그렇기 때문에 OrderItemsDto를 또 따로 만들어서 람다식을 통해서 다시 변환해주는 형식을 사용한다.
그냥 OrderItem 리스트를 stream으로 변환을 하게 되면 이것도 엔티티 노출이 발생하게 되니 또 따로 OrderItemsDto를 만들고 변환을 해야한다.
@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){
orderId = order.getId();
name = order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getOrderStatus();
address = order.getDelivery().getAddress();
orderItems = order.getOrderItems().stream()
.map(orderItem -> new OrderItemDto(orderItem))
.collect(Collectors.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();
}
}
하지만 이렇게 할 경우 N+1문제가 발생한다.
order 조회때 1번
member,address때 N번(order 조회에서 나온 결과만큼)
orderItem찾을때 n번 (order 조회에서 나온 결과 개수만큼)
item N번 쿼리 발생으로 엄청난 쿼리 수 발생
@Data 는 만들어주는게 너무 많기 때문에 사용하지 않는게 좋을 수도 있다. -> getter와 setter는 물론이고 다른것도 꽤 많이 만들어짐.따라 들어가보면
Getter, Setter, RequiredArgsConstructor, ToString, EqualsAndHashCode, Value
밑의 이만큼의 어노테이션을 넣어주는 어노테이션이다. -> 그렇기 때문에 무지성 @Data를 쓰는 건 좋지 않다고 한다.