조회 쿼리에 연관된 필드를 채우기 위해 첫 번째 쿼리 이후에 연관된 fk에 대해서 N개의 쿼리가 추가로 발생하는 문제
먼저 N+1 문제를 터트려 보겠습니다.
위와 같은 데이터가 있을 때 사용자를 조회해봅시다!
즉시로딩 테스트
@ManyToOne(fetch = EAGER) private Team team;
@Test void 사용자_조회() { String jpql = "select u from User u"; List<User> resultList = em.createQuery(jpql, User.class).getResultList(); System.out.println("==========================="); for (User user : resultList) { System.out.println("username : " + user.getUsername()); } System.out.println("==========================="); for (User user : resultList) { System.out.println("username = " + user.getUsername()); Team team = user.getTeam(); System.out.println(team.getClass()); System.out.println("user teamName = " + team.getName()); System.out.println(); } }

=========================== username : 짱구 username : 유리 username : 치타 username : 코난 =========================== username = 짱구 class com.study.jpql.domain.Team user teamName = 해바라기반 username = 유리 class com.study.jpql.domain.Team user teamName = 해바라기반 username = 치타 class com.study.jpql.domain.Team user teamName = 장미반 username = 코난 class com.study.jpql.domain.Team user teamName = 어린이 탐정단
사용자만 조회 했는데 사용자의 팀 정보를 채우기 위해 jpa가 즉시 3개의 팀 조회 쿼리를 발생 시켰습니다.
지연 로딩 테스트
같은 테스트에 대해서 fetch 설정만 lazy로 변경하면 N+1문제가 사라질까요??
@ManyToOne(fetch = LAZY) private Team team;

요청한 쿼리 단 하나만을 잘 실행 했습니다.
이를 통해 사용자의 이름정보 만을 조회 할 때는 아무런 추가 쿼리가 나가지 않았습니다.
=========================== username : 짱구 username : 유리 username : 치타 username : 코난
하지만 사용자의 팀 정보를 조회 할 때는 새로운 팀 정보가 발견 될 때마다 db에 쿼리를 발생 시켜 팀 정보를 조회 합니다. 결과 적으로 루프 시에 3개의 팀 조회 쿼리가 발생합니다.
=========================== username = 짱구 class com.study.jpql.domain.Team$HibernateProxy$ghrxMDXg Hibernate: select team0_.id as id1_1_0_, team0_.teamname as teamname2_1_0_ from team team0_ where team0_.id=? user teamName = 해바라기반 username = 유리 class com.study.jpql.domain.Team$HibernateProxy$ghrxMDXg user teamName = 해바라기반 username = 치타 class com.study.jpql.domain.Team$HibernateProxy$ghrxMDXg Hibernate: select team0_.id as id1_1_0_, team0_.teamname as teamname2_1_0_ from team team0_ where team0_.id=? user teamName = 장미반 username = 코난 class com.study.jpql.domain.Team$HibernateProxy$ghrxMDXg Hibernate: select team0_.id as id1_1_0_, team0_.teamname as teamname2_1_0_ from team team0_ where team0_.id=? user teamName = 어린이 탐정단
즉시로딩과 지연로딩 둘 모두 N+1 문제에서 자유롭지 않습니다.
이를 해결하기 위해서는 fetch join를 활용해야합니다.