[JPA & Hibernate] N + 1 Problems

원알렉스·2020년 8월 23일
0

JPA

목록 보기
12/16
post-thumbnail
post-custom-banner

깃허브 소스코드
Udemy 강의영상

🚀 N + 1 문제란?

특정 데이터를 조회하기 위해 쿼리 1번으로 N개의 데이터를 가져왔는데, 각 데이터를 순회하면서 연관 관계에 있는 데이터를 얻기 위해 쿼리를 N번 더 수행하는 문제입니다.

주로 연관 관계에 대한 FetchType을 Lazy로 지정했을 때 주로 발생합니다.

해결방안 1. FetchType을 Eager로 변경

이 방법을 사용하게 되면 해당 데이터를 조회할 때마다 연관 관계에 있는 데이터까지 같이 조회가 되므로 성능면에서는 Lazy 보다 떨어지므로 비추천합니다.

@Entity
@NamedQueries(
        value = {
                @NamedQuery(name = "query_get_all_courses", query = "SELECT C FROM Course C"),
                @NamedQuery(name = "query_get_all_courses_join_fetch", query = "SELECT C FROM Course C JOIN FETCH C.students S")
        }
)
public class Course {

    @ManyToMany(mappedBy = "courses", fetch = FetchType.Eager)
    private List<Student> students = new ArrayList<>();
    ...
}

해결방안 2. EntityGraph

  • EntityManager로 조회할 데이터를 가지고 EntityGraph를 생성해줍니다.
  • 해당 객체에 같이 조회할 데이터를 추가해줍니다.
  • 쿼리를 생성해줄 때 힌트에 생성한 객체를 추가해줍니다.
@Test
@Transactional
public void solvingNPlusOneProblemWithEntityGraph() {
    EntityGraph<Course> entityGraph = entityManager.createEntityGraph(Course.class);
    entityGraph.addSubgraph("students");

    List<Course> courses = entityManager
            .createNamedQuery("query_get_all_courses", Course.class)
            .setHint("javax.persistence.loadgraph", entityGraph)
            .getResultList();

    courses
            .forEach(course -> LOGGER.info(String.format("Course -> {%s} Students -> {%s}", course, course.getStudents())));
}

해결방안 3. Join Fetch

  • SELECT C FROM Course C JOIN FETCH C.students S
@Test
@Transactional
public void solvingNPlusOneProblemWithJoinFetch() {
    List<Course> courses = entityManager
            .createNamedQuery("query_get_all_courses_join_fetch", Course.class)
            .getResultList();

    courses
            .forEach(course -> LOGGER.info(String.format("Course -> {%s} Students -> {%s}", course, course.getStudents())));
}
profile
Alex's Develog 🤔
post-custom-banner

0개의 댓글