Fetch join ํŽ˜์น˜ ์กฐ์ธ์„ ํ†ตํ•ด N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ด…์‹œ๋‹ค!

 
@Test void ์‚ฌ์šฉ์ž_์กฐํšŒ_ํŽ˜์น˜_์กฐ์ธ() { String jpql = "select u from User u join fetch u.team"; 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(); } }
fetch join ์ ์šฉ (fetch type ์€ ์ƒ๊ด€ ์—†์Œ)
๋ฐœ์ƒ ์ฟผ๋ฆฌ ๋กœ๊ทธ
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
์ถœ๋ ฅ ๋กœ๊ทธ
=========================== 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 = ์–ด๋ฆฐ์ด ํƒ์ •๋‹จ
ํŽ˜์น˜ ์กฐ์ธ์„ ํ†ตํ•ด ๋‹จ ํ•œ๋ฐฉ์˜ join ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ๊ฒฐ๊ณผ ์—”ํ‹ฐํ‹ฐ์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ jpa ๊ฐ€ ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ๋งคํ•‘ ํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
 
๋งŒ์•ฝ ์—ฌ๊ธฐ์„œ fetch ํ‚ค์›Œ๋“œ๋ฅผ jpql์—์„œ ์ œ๊ฑฐํ•˜๋ฉด ๊ฐ™์€ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ jpa๋Š” ์ด๋ฅผ ์ฑ„์›Œ์•ผ ํ•  ๊ทผ๊ฑฐ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์–ป์–ด์˜จ t.teamname ๊ณผ ๊ฐ™์€ team์˜ ํ•„๋“œ๋ฅผ ๊ทธ๋ƒฅ ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
 
์ดํ›„์— ์—ฐ๊ด€๊ด€๊ณ„์˜ fetch ์˜ต์…˜ (EAGER/LAZY)์— ๋”ฐ๋ผ ์ถ”๊ฐ€ ์ ์ธ ์ฟผ๋ฆฌ๋ฅผ ๋ฐœ์ƒ ์‹œํ‚ต๋‹ˆ๋‹ค.

์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ

ํŒ€์„ ์กฐํšŒ ํ•˜๋ฉฐ ์†Œ์†๋œ ๋ฉค๋ฒ„๋ฅผ ๋ชจ๋‘ ์กฐํšŒํ•˜๋Š” @OneToMany์˜ ๊ด€๊ณ„์—์„œ์˜ fetch join๋„ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
@Test void ํŒ€_์กฐํšŒ_ํŽ˜์น˜_์กฐ์ธ() { String jpql = "select t from Team t join fetch t.users"; List<Team> resultList = em.createQuery(jpql, Team.class) .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(); } }
Hibernate: select t.id, u.id, t.teamname, u.team_id u.username u.team_id, u.id from team t inner join user u on team0_.id=users1_.team_id
ํ•œ๋ฐฉ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๊ณ  ํŒ€ ๋ฐ์ดํ„ฐ์˜ ์ปฌ๋ ‰์…˜ ์—ฐ๊ด€๊ด€๊ณ„์ธ users field์— ๋Œ€ํ•ด ๋งคํ•‘์„ ์ž˜ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ดํŽด ๋ณด๋ฉด ๋ปฅํŠ€๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
team = ํ•ด๋ฐ”๋ผ๊ธฐ๋ฐ˜ -> ์งฑ๊ตฌ ์œ ๋ฆฌ team = ํ•ด๋ฐ”๋ผ๊ธฐ๋ฐ˜ -> ์งฑ๊ตฌ ์œ ๋ฆฌ team = ์žฅ๋ฏธ๋ฐ˜ -> ์น˜ํƒ€ team = ์–ด๋ฆฐ์ด ํƒ์ •๋‹จ -> ์ฝ”๋‚œ
ํ•ด๋ฐ”๋ผ๊ธฐ๋ฐ˜์ด ๋‘๊ฐœ๊ฐ€ ๋˜์—ˆ๋‹ค!!!
๋ปฅํŠ€๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•œ ์ด์œ ๋Š” db ์ฟผ๋ฆฌ์˜ ์‹คํ–‰๊ฒฐ๊ณผ๋ฅผ ์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ธˆ๋ฐฉ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ฟผ๋ฆฌ ์‹คํ–‰ ๊ฒฐ๊ณผ
t.id
u.id
t.teamname
u.team_id
u.username
1
1
ํ•ด๋ฐ”๋ผ๊ธฐ๋ฐ˜
1
์งฑ๊ตฌ
1
2
ํ•ด๋ฐ”๋ผ๊ธฐ๋ฐ˜
1
์œ ๋ฆฌ
2
3
์žฅ๋ฏธ๋ฐ˜
2
์น˜ํƒ€
3
4
์–ด๋ฆฐ์ด ํƒ์ •๋‹จ
3
์ฝ”๋‚œ
์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ result set์— ๋‹ด์•„์„œ ํ’€๊ธฐ ๋•Œ๋ฌธ์— jpa ๋ ˆ๋ฒจ์—์„œ๋Š” team ๋ฐ์ดํ„ฐ์˜ ๋ปฅํŠ€๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค. @NtoMany๊ด€๊ณ„์˜ ์–ด์ฉ” ์ˆ˜ ์—†๋Š” ์ˆ™๋ช…์ž…๋‹ˆ๋‹ค.
ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ์ด๋ฅผ ์œ„ํ•œ jpql ํ‚ค์›Œ๋“œ distinct๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
 
distinct ํ‚ค์›Œ๋“œ๋Š” ๋‘๊ฐ€์ง€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฟผ๋ฆฌ์— distinct ํ‚ค์›Œ๋“œ (sql์˜ distinct)
  • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ์ค‘๋ณต ๋ฐ์ดํ„ฐ์˜ ์ œ๊ฑฐ (์ฃผ์†Œ ๊ธฐ๋ฐ˜)
Hibernate: select distinct t.id, u.id, t.teamname, u.team_id u.username u.team_id, u.id from team t inner join user u on team0_.id=users1_.team_id team = ํ•ด๋ฐ”๋ผ๊ธฐ๋ฐ˜ -> ์งฑ๊ตฌ ์œ ๋ฆฌ team = ์žฅ๋ฏธ๋ฐ˜ -> ์น˜ํƒ€ team = ์–ด๋ฆฐ์ด ํƒ์ •๋‹จ -> ์ฝ”๋‚œ
jpql distinct ํ‚ค์›Œ๋“œ ์ ์šฉ ์‹œ ์ถœ๋ ฅ ๋กœ๊ทธ