JPA의 성능을 위해 가장 주의해야 하는 문제이다.

먼저, N+1 문제는 지연로딩, 즉시로딩에서 발생할 수 있다.

N+1문제란?

연관 관계를 갖는 테이블을 쿼리할때 발생하는 이슈로 연관 관계가 설정된 테이블을 조회할 경우 조회된 데이터 개수 (N) 만큼 연관관계를 조회 쿼리가 추가로 발생하게 되어 쿼리가 N+1번 실행되는 이슈

즉시로딩 N+1 문제 (FetchType.EAGER)

즉시로딩의 경우 Entity Manager를 통해 조회할 경우 즉시 연결 데이터를 조인해서 조회하기 때문에 N+1 문제가 발생하지 않는다. 하지만, JPQL을 사용할때 JPQL은 즉시 로딩, 지연 로딩을 고려하지 않기 때문에 문제가 발생하게 된다.

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String name;

    @Embedded
    private Address address;

    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
    private List<Order> orders = new ArrayList<Order>();
    ...
}

List<Member> member = em.createQuery(”select m from Member m”,Member.class).getResultList();

로 조회를 시작하면

  1. select * from member; 쿼리가 실행되고 즉시 로딩으로 인해 바로 아래의 쿼리가 실행된다.
  2. select * from orders where member_id = 1;

만약 for문으로 반복되서 호출되는 경우

  1. select * from orders where member_id = 2;
  2. select * from orders where member_id = 3;
  3. select * from orders where member_id = 4;
  4. select * from orders where member_id = 5;
  5. select * from orders where member_id = 6;

와 같이 쿼리가 실행된다.

지연 로딩 N+1 문제 (FetchType.Lazy)

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String name;

    @Embedded
    private Address address;

    @OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<Order>();
    ...
}