연관 관계에서 발생하는 이슈로
연관 관계가 설정된 엔티티를 조회하는 경우에
조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여
데이터를 읽어오는 현상
N+1 문제가 발생하는 이유는 JPA가 JPQL을 분석해서 SQL을 생성할 때는 글로벌 Fetch 전략을 참고하지 않고 오직 JPQL 자체만을 사용한다.
원하는 쿼리는 select * from () left join () on ().()_id = ().id Fetch join을 사용하면 최적화된 쿼리를 직접 사용할 수 있다.
하지만 jpaRepository에서 제공해주는 기능이 아니므로 JPQL로 직접 작성해야 한다.
Fetch Join의 단점은 우리가 연관관계 설정해놓은 FetchType을 사용할 수 없다는 것이다.
Fetch Join을 사용하게 되면 데이터 호출 시점에 모든 연관 관계의 데이터를 가져오기 때문에 FetchType을 Lazy로 해놓는것이 무의미하다.
하나의 쿼리문으로 가져오다 보니 페이징이 불가능하다.
@EntityGraph 의 attributePaths에 쿼리 수행시 바로 가져올 필드명을 지정하면 Lazy가 아닌 Eager 조회로 가져오게 된다.
사용하지 않는 것을 권장.
@EntityGraph(attributePaths = "cats")
@Query("select o from Owner o")
List<Owner> findAllEntityGraph();
하이버네이트가 제공하는 org.hibernate.annotations.BatchSize 어노테이션을 이용하면 연관된 엔티티를 조회할 때 지정된 size 만큼 SQL의 IN절을 사용해서 조회한다.
즉시로딩이므로 Owner를 조회하는 시점에 Cat를 같이 조회한다.
@BatchSize가 있으므로 Cat의 row 갯수만큼 추가 SQL을 날리지 않고, 조회한 Owner 의 id들을 모아서 SQL IN 절을 날린다.
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Owner {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@BatchSize(size=5)
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
private Set<Cat> cats = new LinkedHashSet<>();
}
https://programmer93.tistory.com/83
https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1