728x90
- SQL 조인 종류 X
- JPQL에서 성능 최적화를 위해 제공하는 기능
- 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
- join fetch 명령어 사용
- 페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인 경로
- 회원을 조회하면서 연관된 팀도 함께 조회(SQL 한 번에)
- SQL을 보면 회원뿐만 아니라 팀(T.*)도 함께 SELECT
- [JPQL] select m from Member m join fetch m.team
- [SQL] SELECT M.*, T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID
페치 조인 사용하기

Test
Team teamA = new Team();
teamA.setName("teamA");
em.persist(teamA);
Team teamB = new Team();
teamB.setName("teamB");
em.persist(teamB);
Member member1 = new Member();
member1.setUsername("회원1");
member1.changeTeam(teamA);
em.persist(member1);
Member member2 = new Member();
member2.setUsername("회원2");
member2.changeTeam(teamA);
em.persist(member2);
Member member3 = new Member();
member3.setUsername("회원3");
member3.changeTeam(teamB);
em.persist(member3);
em.flush();
em.clear();
String query = "select m "
+" from Member m";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
for(Member member : result) {
System.out.println(member.getUsername() + ", " + member.getTeam().getName());
//회원1, 팀A(SQL)
//회원2, 팀A(1차캐시)
//회원3, 팀B(SQL)
//회원 100명 -> N+1
//fetch join 으로 해결할수 밖에 없다
}
실행 결과
Hibernate:
/* select
m
from
Member m */ select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.memberType as memberty3_0_,
member0_.TEAM_ID as team_id5_0_,
member0_.username as username4_0_
from
Member member0_
Hibernate:
select
team0_.id as id1_3_0_,
team0_.name as name2_3_0_
from
Team team0_
where
team0_.id=?
회원1, teamA
회원2, teamA
Hibernate:
select
team0_.id as id1_3_0_,
team0_.name as name2_3_0_
from
Team team0_
where
team0_.id=?
회원3, teamB
페치 조인 사용

실행 결과

- FetchJoin을 사용하면 Lazy 모드지만, 한방 쿼리로 Join 해서 데이터를 가지고 온다.
컬렉션 페치 조인
- 일대다 관계, 컬렉션 페치 조인
JPQL
select t
from Team t join fetch t.members
where t.name = ‘팀A
SQL
SELECT T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'


- 일대다 fetchJoin을 하게되면 다 쪽으로 데이터가 뻥튀기되는 문제가 발생한다.
- 팀 A 에 회원 데이터가 2개 이므로 팀 A 데이터가 2개가 출력된다.
페치 조인과 DISTINCT
SQL의 DISTINCT는 중복된 결과를 제거하는 명령
JPQL의 DISTINCT 2가지 기능 제공
1. SQL에 DISTINCT를 추가
select distinct t
from Team t join fetch t.members
where t.name = ‘팀A’

- SQL에 DISTINCT를 추가하지만 데이터가 다르므로 SQL 결과에서 중복제거 실패
2. 애플리케이션에서 엔티티 중복 제거

- DISTINCT가 추가로 애플리케이션에서 중복 제거시도
- 같은 식별자를 가진 Team 엔티티 제거
- 위험하다 -> 중복된 데이터가 여러 개라면??? , 뒤에 나오지만 @BatchSize를 통해 해결 가능
페치 조인과 일반 조인의 차이
일반 조인 실행 시 연관된 엔티티를 함께 조회하지 않음
일반 조인
JPQL
select t
from Team t join t.members m
where t.name = ‘팀A'
SQL
SELECT T.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'
페치 조인
JPQL
select t
from Team t join fetch t.members
where t.name = ‘팀A'
SQL
SELECT T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A
- JPQL은 결과를 반환할 때 연관관계 고려 X
- 단지 SELECT 절에 지정한 엔티티만 조회할 뿐
- 여기서는 팀 엔티티만 조회하고, 회원 엔티티는 조회 X
- 페치 조인을 사용할 때만 연관된 엔티티도 함께 조회(즉시 로딩)
- 페치 조인은 객체 그래프를 SQL 한 번에 조회하는 개념
- 페치 조인은 연관된 엔티티를 함께 조회함
728x90
'자바 ORM 표준 JPA 프로그래밍(인프런)' 카테고리의 다른 글
| Ch11. 객체지향 쿼리 언어2(중급 문법) - 다형성 쿼리 (0) | 2021.12.18 |
|---|---|
| Ch11. 객체지향 쿼리 언어2(중급 문법) - 페치 조인(한계) (0) | 2021.12.18 |
| Ch11. 객체지향 쿼리 언어2(중급 문법) - 경로 표현식 (0) | 2021.12.18 |
| Ch10. 객체지향 쿼리 언어1(기본 문법) - JPQL 함수 (0) | 2021.12.17 |
| Ch10. 객체지향 쿼리 언어1(기본 문법) - 조건식(CASE 등등) (0) | 2021.12.17 |