JPA : Order와 관련된 API 구현하기
- Spring/JPA
- 2022. 1. 15.
이 글은 인프런의 김영한님의 강의를 듣고 복습하며 정리한 글입니다.
연관관계 복잡한 테이블 API 제작
JPA : JPA를 활용한 회원 관련 API 개발은 간단한 API 개발이었다. 왜냐하면 Member와 관련된 API를 만들기는 했으나, 단순하게 회원 저장했을 때 만들어지는 PK값, 그리고 회원의 이름 정도였기 때문이다. 또한, 회원 조회와 관련된 API는 회원 내부적으로 주문 정보를 가지고 있지 않았기 때문에 null로 표시되었기 때문이다.
이번에는 조금 다르다. 주문은 회원, 배송, 주문_아이템과 연관관계를 가지고 있다. 따라서 회원 API를 개발하는 것보다 복잡하다. 이런 저런 문제가 발생할 수 있다. 하나하나 알아보려고 한다.
알아두기 : Hibernate5Module
@Bean
Hibernate5Module hibernate5Module() {
Hibernate5Module hibernate5Module = new Hibernate5Module();
return hibernate5Module;
}
이 모듈을 Bean으로 등록해두면 JPA가 JSON을 파싱할 때, 프록시 객체는 "null" 값으로 파싱해서 보내준다.
코드 실습
주문 엔티티로 조회, 주문 엔티티로 반환하는 API
@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1() {
List<Order> orders = orderService.findOrders(new OrderSearch());
orders.forEach(order ->
{
order.getMember().getName();
order.getDelivery().getStatus();
});
return orders;
}
- 처음 위 코드를 실행하면 에러가 발생한다
- 에러의 이유는 JSON을 만드는 과정에서 양방향 관계에서 순환 참조가 일어나기 때문이다.
- order는 member를 가진다. member는 List<Order>를 가진다. 이 때문에 순환 참조가 계속 발생한다.
- 순환 참조를 없애 주기 위해서 한쪽 방향에는 @JsoinIgnore를 달아준다.
- @JsonIgnore는 Jackson 라이브러리가 JSON을 파싱할 때, 해당 어노테이션이 있으면 무시해준다.
- order.getMember().getName()들은 지연 로딩으로 불러온 값들이 프록시 상태이기 때문에 초기화를 해주기 위함이다.
- @JsonIgnore를 쓰지 않는다면, Hibernate5Module을 통해서 모든 객체의 지연 로딩을 강제 초기화 해줄 수 있다.
@Bean
Hibernate5Module hibernate5Module() {
Hibernate5Module hibernate5Module = new Hibernate5Module();
hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true); // 이렇게 하면 Lazy 로딩으로 되어있는 애들도 다 불러와져서 JSON된다.
return hibernate5Module;
}
실행 결과
Order에 대한 쿼리를 1번 했다. 실제로 Order 쿼리가 나간 후 Member를 조회하는 쿼리 2번, Delivery를 조회하는 쿼리가 2번이 나갔다. 왜냐하면 Order에는 총 2건이 있기 때문이고, Order를 불러와서 각각 프록시 객체를 초기화 하는 과정이 있었기 때문에 Member와 Delivery에 각각 쿼리가 나가게 된 것이다.
여담이지만 JPQL 쿼리는 쿼리가 적혀있는 것 그대로 나간다. 즉, FETCH JOIN 같은 것이 아니라 ORDER TABLE을 그대로 가져오는 것이다. 이후 ORDER_ITEMS에 대한 쿼리도 2번 나가는데, 이 부분은 잘 이해를 못했다. 이상한게 ORDER_ITEM에 대한 프록시 초기화라고 생각을 했었는데, 포스트맨에서도 볼 수 있지만 OrderItems의 값은 Null이다.
결론
- Hibernate5Module을 통해 관련된 객체들의 전체 강제 지연로딩을 하게 되면 나가는 데이터가 엄청 많다. 즉, 네트워크 낭비다.
- 엔티티들을 직접 반환해주기 떄문에 변화에 취약하다.
- 지연 로딩 이후, 프록시를 초기화 해주는 과정에서 N+1 문제가 발생한다.
주문 엔티티로 조회, DTO로 반환하는 API
'Spring > JPA' 카테고리의 다른 글
JPA : 객체의 영속성 컨텍스트 참조 (0) | 2022.02.05 |
---|---|
스프링 JPA : Open Session In View (0) | 2022.01.17 |
JPA : JPA를 DTO로 바로 접근하기 (0) | 2022.01.15 |
JPA : JPA를 활용한 회원 관련 API 개발 (0) | 2022.01.15 |
JPA : @Transacitonal 관련 정리 (0) | 2022.01.10 |