JPA로 애플리케이션을 개발할 때 성능상 가장 주의해야 하는 것이 N+1 문제다.
@Enttiypublic class Member{ @Id @GeneratedValue private Long id; @OneToMany(mappedBy = "member", fetch = FetchType.EAGER) private List<Order> orders = new ArrayList<Order>(); ...}
@Entity@Table(name = "ORDERS")public class Order{ @Id @GeneratedValue private Long id; @ManyToOne private Member member; ...}
회원과 주문정보는 1:N, N:1 양방향 연관관계.
회원이 참조하는 주문정보(Member.orders)를 즉시 로딩으로 설정.
특정 회원 하나를 em.find()로 조회하면 즉시 로딩으로 설정한 주문정보도 함께 조회한다.
em.find(Member.class, id);--실행된 SQLSELECT M.*, O.*FROM Member m OUTER JOIN ORDERS OON M.ID = O.MEMBER_ID
SQL을 두 번 실행하는 것이 아니라 조인을 이용해 SQL을 한 번의 SQL로 회원과 주문정보를 함께 조회한다.
여기까지만 보면 즉시 로딩이 상당히 좋아보인다.
문제는 JPQL을 사용할 때 발생하는데
String jpql = "SELECT m FROM Member m";List<Membe> members = em.createQuery(jpql, Member.class).getResultList();
JPQL을 실행하면 JPA는 분석 후 SQL을 생성한다.
이때는 즉시 로딩과 지연 로딩에 대해 전혀 신경쓰지 않고 JPQL만 사용해서 SQL을 생성한다.
SELECT * FROM MEMBER
SQL의 실행 결과로 먼저 회원 엔티티를 애플리케이션에 로딩한다.
회원 엔티티와 연관된 주문 컬렉션이 즉시 로딩으로 설정되어 있어 JPA가 주문 컬렉션을 즉시 로딩하려고 다음 SQL을 추가로 실행한다.