HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📝
남득윤 학습 저장소
/
JPA
JPA
/
➕
JPA N+1 문제
/
페지 조인의 한계 - 컬렉션 연관관계 에서의 페이징 처리

페지 조인의 한계 - 컬렉션 연관관계 에서의 페이징 처리

@NtoMany 컬렉션 연관관계 에서의 페이징 처리

이번에는 바로 위 컬렉션 페치 조인에 페이징 처리를 추가 해 보겠습니다.
@Test void 팀_조회_페치_조인_페이징() { String jpql = "select t from Team t join fetch t.users"; List<Team> resultList = em.createQuery(jpql, Team.class) .setMaxResults(2) .getResultList(); for (Team team : resultList) { System.out.print("team = " + team.getName()); System.out.print(" -> "); for (User user : team.getUsers()) { System.out.print(user.getUsername() + " "); } System.out.println(); } }
2022-05-13 17:24:22.671 WARN 3888 --- [ main] o.h.h.internal.ast.QueryTranslatorImpl : HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! Hibernate: select u.id, t.id u.team_id u.username, t.teamname from user u inner join team t on u.team_id=t.id team = 해바라기반 -> 짱구 유리 team = 장미반 -> 치타
해바라기반과 장미반 - 두 반을 페이징 해서 의도 대로 잘 된것인가 싶지만
sql로그를 보면 limi, offset 처리가 없습니다.
 
WARN 로그를 살펴보면 collection fetch에 페이징 처리가 적용되어 일단 전체 조회 쿼리를 한 다음에 메모리에서 offset limit 을 적용한 것을 알 수 있습니다. 쿼리 실행 결과를 생각해보면 페이징 처리가 되지 않은 이유을 알 수 있습니다.
t.id
u.id
t.teamname
u.team_id
u.username
1
1
해바라기반
1
짱구
1
2
해바라기반
1
유리
2
3
장미반
2
치타
3
4
어린이 탐정단
3
코난
언뜻 보면 모든 데이터를 땡겨 오더라도 하이버네이트가 페이징 처리를 메모리에서 해 주기때문에 아무 문제 없어 보이지만 데이터가 1000만건이라면 어떨까요? 순식간에 OOM이 발생하고 서버가 죽어 버릴것입니다.
 

컬렉션 연관관계 페이징 처리 해결책 : Batch Size

해결책은 fetch 조인을 사용하지 않는 것이다.
@Test void 팀_조회_배치_사이즈_페이징() { String jpql = "select distinct t from Team t join t.users"; List<Team> resultList = em.createQuery(jpql, Team.class) .setMaxResults(3) .getResultList(); for (Team team : resultList) { System.out.println("\nteam = " + team.getName()); System.out.print(" -> "); for (User user : team.getUsers()) { System.out.print(user.getUsername() + " "); } System.out.println(); } }
사실 팀 데이터가 총 3개라 페이지 처리 하지 않은것과 같지만 배치 사이즈의 동작 방식에만 집중해주세용
Hibernate: select distinct t.id, t.teamname from team t inner join user u on team0_.id=users1_.team_id limit 3 team = 해바라기반 -> Hibernate: select u.team_id, u.id, u.team_id u.username from user u where u.team_id in (1, 2) 짱구 유리 team = 장미반 -> 치타 team = 어린이 탐정단 -> Hibernate: select u.team_id, u.id, u.team_id u.username from user u where u.team_id = 3 코난
배치 사이즈를 통해 Lazy, 혹은 Eager 로딩에 따라 발생하는 추가 쿼리에서 where 절의 fk 대상을 in을 통해 batch-size 만큼 설정 할 수 있다.