Member와 Order 엔티티의 연관관계는 1:N이다
여기서 Member엔티티에서 Orders를 조회할때 즉시로딩으로 설정해보았다.
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Order> orders = new ArrayList<>();
~ToMany의 연관관계는 기본적으로 지연로딩이라 즉시로딩(EAGER)를 수동으로 설정하고 조회한다.
public List<Member> testMember(Long userId){
return em.createQuery("select m from Member m",Member.class).getResultList();
}
분명 Member엔티티만 조회했는데 그와 연관된 order엔티티가 조회되었다. 즉시로딩으로 설정하였기 때문이다.
이때 Member 엔티티의 조회결과수(N) 만큼 order엔티티를 조회하는 쿼리가 발생된다. 멤버엔티티를 조회(1) + 멤버엔티티의 결과수만큼 조회(N) 이것이 N+1문제이다.
즉시로딩을 지연로딩으로 변경해본다.
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
toMany연관관계는 디폴트가 지연로딩이라 굳이 LAZY를 명시할 필요가 없다.
그 후 조회 로직을 수정해본다.
public List<Member> testMember(Long userId){
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("----------------------------------------");
members.stream().forEach(m -> m.getOrders().stream().forEach(o-> o.getId()));
return members;
}
멤버엔티티조회 후 경계선을 출력하고 그다음 각각 멤버엔티티의 주문정보를 조회한다.
멤버엔티티를 조회했을때까지는 딱 한번만 쿼리가 호출된다.
하지만 경계선이 후 해당 멤버엔티티들의 주문정보 로직을 호출하게되면
즉시로딩과 마찬가지로 N+1이 발생된다. 지연로딩일 경우 해당 연관관계의 엔티티를 프록시로 가지고있다가 연관관계의 엔티티의 필드를 꺼내었을때 쿼리에서 조회하여 세팅하게된다. 즉 지연로딩일지라도 해당 연관엔티티를 사용하게된다면 N+1문제를 근본적으론 해결할 수 없다.
JPA를 예전에 공부했고 복습하는 과정이기 때문에 해당문제를 해결 하는 방법은 알고 있지만 그것은 나중에 다시 블로그에 기록하며 복습하도록 해야겠다.