JPA n+1 문제

Posted by Yun on 2017-12-03

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(); // 연과된 모든 엔티티를 조회한다.
  • 실행된 SQL은 다음과 같다.
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을 만든다.
코드를 분석하먄 내부에서 다음과 같은 순서로 동작한다.

  1. select o from Order o JPQL을 분석해서 select * from Order SQL을 생성한다.
  2. 데이터베이스에서 결과를 받아 order 엔티티 인스턴스를 생성한다.
  3. Order.member의 글로벌 페지 전략이 즉시 로딩이므로 order를 로딩히는 즉시 연관된 member로 로딩해야한다.
  4. 연관된 Member를 영속성 컨텍스트에서 찾는다.
  5. 만약 영속성 컨텍스트에 없으면 select * from member where id? SQL을 조회한 order 엔티티 수만큼 실행한다.