JPA N + 1 문제
1 2 3 4
| select o.* m.* from Order o left outer join Member m on o.MEMBER_ID=m.MEMBER_ID where o.id =1
|
- 실행된 SQL을 보면 즉시 로딩으로 설정한 member 엔티티를 조인 쿼리로 함께 조회한다. 여기까지 보면 글로벌 즉시 로딩 전략이 상당히 좋아보이지만 문제는 JPQL을 사용할 때 발생한다.
- 위처럼 즉시 로딩으로 설정했다고 가정하고 JPQL로 조회 해보자
1 2
| List<Order> orders = em.createQuery("select o from Order o", Order.class) .getResultList();
|
1 2 3 4 5
| select * from Order // JPQL로 실행된 SQL select * from Member where id = ? // EAGER 로 실행된 SQL select * from Member where id = ? // EAGER 로 실행된 SQL select * from Member where id = ? // EAGER 로 실행된 SQL select * from Member where id = ? // EAGER 로 실행된 SQL
|
JPA가 JPQL을 분석해서 SQL을 생헐 할 때 글로벌 패치 전략을 참고하지 않고 오직 JPQL 자체만 사용한다. 따라서 즉시 로딩이든 지연 로딩이든 구분하지 않고 JPQL 쿼리 자체에 충실하게 SQL을 만든다.
코드를 분석하먄 내부에서 다음과 같은 순서로 동작한다.
select o from Order o
JPQL을 분석해서 select * from Order
SQL을 생성한다.
- 데이터베이스에서 결과를 받아 order 엔티티 인스턴스를 생성한다.
- Order.member의 글로벌 페지 전략이 즉시 로딩이므로 order를 로딩히는 즉시 연관된 member로 로딩해야한다.
- 연관된 Member를 영속성 컨텍스트에서 찾는다.
- 만약 영속성 컨텍스트에 없으면
select * from member where id?
SQL을 조회한 order 엔티티 수만큼 실행한다.