Gunju-Ko / TIL

Today I Learn
0 stars 0 forks source link

JPA - N+1 문제 #43

Open Gunju-Ko opened 2 years ago

Gunju-Ko commented 2 years ago

N+1 문제

즉시 로딩과 N+1

@Entity
@Table(name = "MEMBER")
public class Member {
    @Id @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
    private List<Order> orders = new ArrayList<Order>();
}
@Entity
@Table(name = "ORDER")
public class Order {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne
    private Member member;
}

특정 회원 하나를 em.find() 메소드로 조회하면 즉시 로딩으로 설정한 주문 정보도 함께 조회한다. 이 때 실행된 SQL을 보면 조인을 사용해서 한 번의 SQL로 회원과 주문 정보를 함께 조회한다. 하지만 JPQL을 사용할 때 문제가 발생한다.

em.createQuery("select m from Member m", Member.class).getResultList();

JPQL을 실행하면 즉시 로딩과 지연 로딩에 대해서 전혀 신경 쓰지 않고 JPQL만 사용해서 SQL을 생성한다. 이 때 N+1 문제가 발생한다.

SELECT * FROM MEMBER
SELECT * FROM ORDER WHERE MEMBER_ID = 1
SELECT * FROM ORDER WHERE MEMBER_ID = 2
SELECT * FROM ORDER WHERE MEMBER_ID = 3
SELECT * FROM ORDER WHERE MEMBER_ID = 4
... 

회원 조회 SQL로 N명의 회원 엔티티를 조회하면, 각각의 회원 엔티티와 연관된 주문 콜렉션을 즉시 조회하기 위해 추가로 N번의 SQL을 실행한다. 이처럼 처음 실행한 SQL의 결과 수 만큼 추가로 SQL을 실행하는 것을 N+1 문제라 한다.

지연 로딩과 N+1

@Entity
@Table(name = "MEMBER")
public class Member {
    @Id @GeneratedValue
    private Long id;

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

N+1 문제 해결

페치 조인 사용

select m from Member m join fetch m.orders

하이버네이트 @BatchSize

@Entity
@Table(name = "MEMBER")
public class Member {
    @Id @GeneratedValue
    private Long id;

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

하이버네이트 @Fetch(FetchMode.SUBSELECT)

@Entity
@Table(name = "MEMBER")
public class Member {
    @Id @GeneratedValue
    private Long id;

    @Fetch(FetchMode.SUBSELECT)
    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
    private List<Order> orders = new ArrayList<Order>();
}
SELECT O FROM ORDERS O 
WHERE O.MEMBER_ID IN (SELECT M.ID FROM MEMBER M WHERE M.ID > 10) 

출처

참고하면 좋은글

Gunju-Ko commented 2 years ago

@ManyToOne@BatchSize

@ManyToOne
@BatchSize(size = 5)
private User owner;
@Entity
@BatchSize(size = 5)
public class User {
  ...
}

출처