"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
컬렉션 페치 조인
두 객체 간의 관계가 일대다 연관관계일 경우 컬렉션 페치 조인을 할 수 있다.
- 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'
아래의 코드를 한 번 실행시켜 보자.
- JpaMain.java
String query = "select t from Team t join fetch t.members";
List<Team> result = em.createQuery(query, Team.class)
.getResultList();
for (Team team : result){
System.out.println("team = " + team.getName() + "| members = " + team.getMembers().size());
}
- Hibernate SQL
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
team = 팀A| members = 2
team = 팀A| members = 2 /* 팀A 가 왜 두번씩이나 출력되는 걸까?*/
team = 팀B| members = 1
위의 Hibernate SQL 을 보면 팀A 에 대한 결과가 두번이나 출력되는 것을 확인할 수 있다.
도대체 왜 이렇게 된 것일까?
아래의 그림을 한번 살펴보자.
데이터 베이스에서 조인을 하게 되면 각 필드 별로 속성이 같은 데이터들이 한 항목으로 통합되어서 나타나는게 아니라, 각 데이터마다 따로따로 테이블에 나타나기 때문에 위의 코드에서 페치 조인 결과 회원 두명이 있는 팀A 의 결과가 두 번 나오게 된 것이다.
즉, 조회한 컬렉션에는 위의 그림과 같이 같은 주소값을 가지고 있는 결과가 두 줄이 나오게 된다.
다음과 같은 코드를 작성하면 똑같은 주소값이 두 번 출력되었다는 것을 명확히 알 수 있다.
- JpaMain.java
for (Team team : result){
System.out.println("team = " + team.getName() + "| members = " + team.getMembers().size());
for (Member member : team.getMembers()){
System.out.println("-> member = " + member);
}
}
- Hibernate SQL
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
team = 팀A| members = 2
-> member = Member{id=3, username='회원1', age=0}
-> member = Member{id=4, username='회원2', age=0}
team = 팀A| members = 2
-> member = Member{id=3, username='회원1', age=0}
-> member = Member{id=4, username='회원2', age=0}
team = 팀B| members = 1
-> member = Member{id=5, username='회원3', age=0}
그렇다면 이런 중복은 어떻게 제거해 줄 수 있을까?
- 페치 조인과 DISTINCT
SQL 의 DISTINCT 는 중복된 결과를 제거하는 명령이다.
그런데 SQL 의 DISTINCT 는 데이터의 모든 필드 값들이 완전히 똑같아야 중복 제거를 해주는데 반해, 페치 조인으로 인해 생성된 테이블의 경우 같은 팀에 소속되어 있는 회원 끼리라고 해도 각 회원의 ID 값, 즉 내부 조인된 Member 객체의 기본키 값이 서로 다르기 때문에 데이터가 완전히 똑같지 않게 되어 중복을 제거할 수 없게 되는 것이다.
위와 같은 이유로 SQL 쿼리만으로는 데이터가 줄여지지 않기 때문에 JPA 에서는 DISTINCT 가 추가로 애플리케이션 에서 중복 제거를 시도한다.
그를 통해 같은 식별자를 가진 Team Entity 가 제거되는 것이다.
- JPQL 의 DISTINCT 는 2가지 기능을 제공한다.
1. SQL 에 DISTINCT 를 추가한다.
2. 애플리케이션에서 Entity 중복을 제거한다.
- JpaMain.java
String query = "select distinct t from Team t join fetch t.members";
List<Team> result = em.createQuery(query, Team.class)
.getResultList();
System.out.println("result = " + result.size());
for (Team team : result){
System.out.println("team = " + team.getName() + "| members = " + team.getMembers().size());
for (Member member : team.getMembers()){
System.out.println("-> member = " + member);
}
}
- Hibernate SQL
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
result = 2
team = 팀A| members = 2
-> member = Member{id=3, username='회원1', age=0}
-> member = Member{id=4, username='회원2', age=0}
team = 팀B| members = 1
-> member = Member{id=5, username='회원3', age=0}
위의 Hibernate SQL 을 보면 DISTINCT 명령어를 통해 중복되는 데이터 출력이 제거되었음을 알 수 있다.
다음번 글에서는 페치 조인과 일반 조인의 차이점에 대해 알아보자.
'JPA' 카테고리의 다른 글
JPQL - JPQL 페치 조인(4) (0) | 2020.11.28 |
---|---|
JPQL - JPQL 페치 조인(3) (0) | 2020.11.28 |
JPQL - JPQL 페치 조인(1) (0) | 2020.11.28 |
JPQL - 경로 표현식 (0) | 2020.11.28 |
JPQL - JPQL 기본(10) (0) | 2020.11.24 |