1. 기본개념
2. 엔티티 페치조인
//(1)지연로딩
String query = "select m from Member m";
List<Member> res = em.createQuery(query, Member.class).getResultList();
for (Member member : res) {
System.out.println("member = " + member.getUsername() + member.getTeam().getName());
//member1: SQL(teamA)
//member2: 1차캐시(teamA)
//member3: SQL(teamB)
//N + 1 문제 발생
}
//(2)엔티티 페치조인
String query2 = "select m from Member m join fetch m.team";
List<Member> res2 = em.createQuery(query2, Member.class).getResultList();
for (Member member : res2) {
System.out.println("member with fetch join = " + member.getUsername() + member.getTeam().getName());
//1번의 쿼리로 실행(프록시가 아닌 실제 엔티티, 모두 영속성 컨텍스트에서 관리)
}
Hibernate:
/* select
m
from
Member m
join
fetch m.team */ select
member0_.Id as id1_0_0_,
team1_.Id as id1_3_1_,
member0_.age as age2_0_0_,
member0_.TEAM_ID as team_id5_0_0_,
member0_.type as type3_0_0_,
member0_.username as username4_0_0_,
team1_.name as name2_3_1_
from
Member member0_
inner join
Team team1_
on member0_.TEAM_ID=team1_.Id
member = 회원1팀A
member = 회원2팀A
member = 회원3팀B
3. 컬렉션 페치조인
//(3)컬렉션 페치조인
String query3 = "select t from Team t join fetch t.members";
List<Team> res3 = em.createQuery(query3, Team.class).getResultList();
for (Team team : res3) {
System.out.println("member with fetch join = " + team.getName() + "/" + team.getMembers().size());
//컬렉션(멤버) 수에 맞춰서 중복 로우가 조회됨
//teamA/2
//teamA/2
//teamB/1
}
Hibernate:
/* select
t
from
Team t
join
fetch t.members */ select
team0_.Id as id1_3_0_,
members1_.Id as id1_0_1_,
team0_.name as name2_3_0_,
members1_.age as age2_0_1_,
members1_.TEAM_ID as team_id5_0_1_,
members1_.type as type3_0_1_,
members1_.username as username4_0_1_,
members1_.TEAM_ID as team_id5_0_0__,
members1_.Id as id1_0_0__
from
Team team0_
inner join
Member members1_
on team0_.Id=members1_.TEAM_ID
member with fetch join = 팀A/2
member with fetch join = 팀A/2
member with fetch join = 팀B/1
4. 페치조인과 DISTINCT
//(4)페치조인 + distinct => SQL에 distinct 추가(사실 데이터가 다르므로 유효X) + 애플리케이션에서 중복 제거
String query4 = "select distinct t from Team t join fetch t.members";
List<Team> res4 = em.createQuery(query4, Team.class).getResultList();
for (Team team : res4) {
System.out.println("member with fetch join = " + team.getName() + "/" + team.getMembers().size());
//같은 식별자를 가진 엔티티 제거
//teamA/2
//teamB/1
}
Hibernate:
/* select
distinct t
from
Team t
join
fetch t.members */ select
distinct team0_.Id as id1_3_0_,
members1_.Id as id1_0_1_,
team0_.name as name2_3_0_,
members1_.age as age2_0_1_,
members1_.TEAM_ID as team_id5_0_1_,
members1_.type as type3_0_1_,
members1_.username as username4_0_1_,
members1_.TEAM_ID as team_id5_0_0__,
members1_.Id as id1_0_0__
from
Team team0_
inner join
Member members1_
on team0_.Id=members1_.TEAM_ID
member with fetch join = 팀A/2
member with fetch join = 팀B/1
5. 페치 조인 vs 일반 조인