@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(); } }
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 코난