반응형
package jpabook.jpashop.api;
import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderStatus;
import jpabook.jpashop.repository.*;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
/**
* xToOne(ManyToOne, OneToOne) 관계 최적화
* Order
* Order -> Member
* Order -> Delivery
*/
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
/**
* V1. 엔티티 직접 노출
* - Hibernate5Module 모듈 등록, LAZY=null 처리
* - 양방향 관계 문제 발생 -> @JsonIgnore
*/
@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
for (Order order : all) { //이게 없다면 강제 지연 로딩 설정(JpashopApplication에 설정해둔 것)을 해야 하는데
//이것은 전체 다 하는 거기 때문에그러면 원하지 않는 것들도 다 뿌려진다.
//그래서 따로 for루프를 돌리면서 필요한 것들만 강제 지연 로딩을 해준다
order.getMember().getName(); //order.getMember() 여기 까지는 프록시 객체 -> .getName() 이거까지 하면 Lazy 강제 초기화 -> 멤버에 쿼리를 날려서 JPA(hibernate)가 다 끌고옴
order.getDelivery().getAddress(); //Lazy 강제 초기화
}
return all; //만약 위의 for문이 없이 이대로 반환한다면 Order -> Member -> Order -> Member ... 무한루프에 빠지게 됌(양방향 연관관계의 문제)
//양방향 연관관계의 문제를 해결하기 위해서 둘 중 하나는 @JsonIgnore를 해줘야함. 그래서 Order와 Member중 member에 추가함.
//그리고 OrderItem에도 Order로 가는게 있기 때문에 여기에도 @JsonIgnore 추가 / Delivery에도 추가
//이렇게 양방향인 것 중 하나에 다 @JsonIgnore 추가
}
/**
* V2. 엔티티를 조회해서 DTO로 변환(fetch join 사용X)
* - 단점: 지연로딩으로 쿼리 N번 호출
*/
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2() {
//ORDER 2개
//N + 1 -> 1 + 회원 N + 배송 N = 5
List<Order> orders = orderRepository.findAllByString(new OrderSearch()); //이걸 이대로 반환하면 안되고 <SimpleOrderDto> 이 타입으로 바꿔야 함
List<SimpleOrderDto> result = orders.stream() //그러기 위해서 for문도 가능한데 여기서는 스트림 사용
.map(o -> new SimpleOrderDto(o))//바꿔치기
.collect(Collectors.toList());
return result;
}
@Data
static class SimpleOrderDto { //API 스펙을 명확하게 규정
private Long orderId;
private String name;
private LocalDateTime orderDate; //주문시간
private OrderStatus orderStatus;
private Address address;
public SimpleOrderDto(Order order) {
orderId = order.getId();
name = order.getMember().getName(); //LAZY 초기화 - 영속성 콘텍스트가 memberId를 가지고 영속성 콘텍스트에 찾아봄. 없으면 DB 쿼리에 날림
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress(); //LAZY 초기화
}
}
}
'JPA' 카테고리의 다른 글
[JPA] 연관 관계 매핑에 대하여 (2) | 2024.09.04 |
---|---|
[JPA] 기본키 생성 전략 - @Id, @GeneratedValue (0) | 2024.09.04 |
API 개발 고급 - 준비 (0) | 2023.05.29 |
API 개발 기본 (0) | 2023.05.29 |
웹 계층 개발 (0) | 2023.05.21 |