이 문제는 실무에서 JPA를 사용하려면 100% 으로 이해해야한다!
V1 버전 :엔티티 직접 노출하는 경우
Order 와 Member 는 ManyToOne 관계
Order 와 delivery 는 OneToOne관계
(Order와 OderItems는 OneToMany 관계 -> 이건 복잡하기 떄문에 다음 강의에서 설명)
OrderRepository에 만들어둔 findAllByString을 통해서 List로 그냥 받아서 넘겨준다.
@GetMapping("/api/v1/sample-orders")
public List<Order> ordersV1(){
List<Order> all = orderRespository.findAllByString(new OrderSearch());
return all;
}
근데 이렇게 할 경우 postMan에서 해당 Get을 보내는 경우 무한루프에 빠지게 된다.
이렇게 첫번째 문제를 해결하자 이제 다음 문제가 또 발생해버렸다.
Type definition error 뭐시기 뭐시기하면서 오류가 발생한다.
Order를 가져올때 Order안에 Member가 Lazy로딩이기 때문에 Member를 가져오지 않고 Hibernate에서 가짜 프록시 멤버 객체를 생성해서 넣어준다고한다.(Proxy의 경우 JPA 기본편에서 설명해준다.)
Jackson 라이브러리는 기본적으로 프록시 객체를 json으로 어떻게 생성해야하는지 모른다.
그렇기 때문에 hibernate5Module을 스프링 빈으로 등록을 통해서 생성을 해준다.
구글에서 구글링 해보니 hibernate5Module 깃 주소가 나옴 : https://github.com/FasterXML/jackson-datatype-hibernate/blob/master/hibernate5/src/main/java/com/fasterxml/jackson/datatype/hibernate5/Hibernate5Module.java
build.gradle에 hibernate5Module을 추가
현재와 같이 나오게 된다. member와 orderItems,delivery가 null 인경우는 이 3가지 객체가 Lazy 로딩인데 hibernate5Module의 기본설정이 지연로딩은 무시하고 진행되기 때문에 null로 반환 되는 것이다.
강제로 Lazy로딩을 시켜서 값을 가져올 수는 있다. hibernate5Module의 빈 값에 설정해주면되긴하지만 엔티티를 전부 노출하는건 좋은 방법이 아니기 때문에 알고만 하고 넘어가도록하자.
다른 방법으로는 주문 조회한 all 을 이와같이 for문을 돌려서 member와 delivery의 Lazy를 강제 초기화하는 방법이다.
@GetMapping("/api/v1/sample-orders")
public List<Order> ordersV1(){
List<Order> all = orderRespository.findAllByString(new OrderSearch());
for(Order order: all){
order.getMember().getName(); //Lazy 강제 초기화
order.getDelivery().getAddress();//Lazy 강제 초기화
}
return all;
}
member와 delivery는 제대로 출력되는 모습
orderItems는 강제초기화를 하지 않았기 때문에 null로 표시되는 모습