"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com

페치 조인과 일반 조인의 차이점?
- 일반 조인은 실행 시 연관된 Entity 를 함께 조회하지 않는다.
: JPQL 은 결과를 반환할 때 연관관계를 고려하지 않는다, 단지 SELECT 절에 지정한 Entity 만 조회할 뿐이다.
여기서는 Team Entity 만 조회하고, 회원 Entity 는 조회하지 않는다.
- 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
- JpaMain.java
String query = "select t from Team t join t.members m";
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
t
from
Team t
join
t.members m */ select
team0_.id as id1_3_,
team0_.name as name2_3_ /* 일반 조인으로 실행되었기 때문에 연관된 Entity 를 함께 조회하지 않고 Team 객체 데이터만 조회했다.*/
from
Team team0_
inner join
Member members1_
on team0_.id=members1_.TEAM_ID
result = 3
Hibernate:
select
members0_.TEAM_ID as team_id5_0_0_,
members0_.id as id1_0_0_,
members0_.id as id1_0_1_,
members0_.age as age2_0_1_,
members0_.TEAM_ID as team_id5_0_1_,
members0_.type as type3_0_1_,
members0_.username as username4_0_1_
from
Member members0_
where
members0_.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}
Hibernate:
select
members0_.TEAM_ID as team_id5_0_0_,
members0_.id as id1_0_0_,
members0_.id as id1_0_1_,
members0_.age as age2_0_1_,
members0_.TEAM_ID as team_id5_0_1_,
members0_.type as type3_0_1_,
members0_.username as username4_0_1_
from
Member members0_
where
members0_.TEAM_ID=?
team = 팀B| members = 1
-> member = Member{id=5, username='회원3', age=0}
* 페치 조인을 사용할 때만 연관된 Entity 도 함께 조회한다.(즉시 로딩)
* 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념이다.
페치 조인의 특징과 한계
- 페치 조인 대상에는 원칙적으로 별칭을 줄 수 없다.
String query = "select t from Team t join fetch t.members as m"; // 이와 같은 별칭 부여는 원칙적으로 불가능하다.
자세히 말하자면, 별칭을 준 후 그 별칭을 통해 객체 그래프 탐색등을 해서는 안된다는 뜻이다.
왜 그럴까?
페치 조인이라는 건 기본적으로 연관된 객체 데이터를 다 긁어 오는 것이다. 중간에 조건을 걸어서 필터링 할 수 없다.
만약 전체 데이터에서 필터링을 통해 조건에 맞는 데이터를 가져오고 싶은 경우엔 페치 조인을 사용해서는 안된다.
(따로 쿼리를 만들어서 조회해야 한다.)
그렇다면 왜 따로 조회해야 할까?
Team 과 연관된 회원이 5명인데, 그 중 한명만 불러올 경우 여기서 조작을 잘못하게 되면 나머지 4명의 데이터가 누락되어 있기 때문에 예상과 달리 이상하게 동작할 수 있다.
위와 같은 이유들 때문에 페치 조인은 별칭을 주지 않는 것이 관례이다.
Hibernate 는 가능하나, 가급적 사용하지 않는 걸 추천한다.
- 둘 이상의 컬렉션은 페치 조인 할 수 없다.
둘 이상의 컬렉션을 페치 조인하게 되면 안 그래도 일반 페치 조인만으로 데이터가 부풀려질 수 있는데 페치 조인을 한번 더 하는 것으로 연관관계가 사실상 일대다대다 의 형식이 되어서 데이터가 정말 예상치 못한 방향으로 이상하게 튈 수 있다.
- 컬렉션을 페치 조인하면 페이징 API 를 사용할 수 없다.
데이터가 부풀려지는 상황에서 페이징 API 를 통해 필터링을 걸게 된다면 정확한 데이터의 집계가 되지 않게 된다.
예를 들어 본래 2개의 같은 값을 가지는 필드들이 있는데, 페이징의 기준 사이즈를 1로 잡아버리면 둘 중 하나만 검출되게 되어, 본래는 2개의 필드가 있음에도 불구하고 데이터가 하나만 존재하는 것처럼 결과가 반환되는 것이다.
일대일, 다대일 같은 단일 값 연관 필드들은 페치 조인해도 페이징이 가능하지만, 일대다 와 같은 컬렉션 페치 조인의 경우 Hibernate 는 경고 로그를 남기고 메모리에서 페이징을 하나 매우 위험하다.
- JpaMain.java
String query = "select t from Team t join fetch t.members m";
// 페치 조인에서 페이징 API 를 사용하는 경우 문제점
List<Team> result = em.createQuery(query, Team.class)
.setFirstResult(0)
.setMaxResults(1)
.getResultList();
- Hibernate SQL
WARN: HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
Hibernate:
/* select
t
from
Team t
join
fetch t.members m */ 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
위의 Hibernate SQL 에서 경고 문구 이후 생성된 SQL 문을 보면 페이징 API 를 사용하였음에도 불구하고 페이징을 하는 쿼리가 존재하지 않는다.
즉, DB 에서 Team 에 관한 데이터들을 페이징과 상관없이 모두 가져온 것이다.
이렇게 되면 딱 장애 나기 좋다.
그냥 페치 조인을 하는 경우 페이징은 절대로 하면 안된다.
그럼에도 불구하고 어떻게든 페이징을 사용해야 한다면 다음 번 글에서 컬렉션 페치 조인시 페이징을 하는 방법을 알아보자.
'JPA' 카테고리의 다른 글
JPQL - JPQL 의 여러가지 기능들(1) (0) | 2020.11.29 |
---|---|
JPQL - JPQL 페치 조인(4) (0) | 2020.11.28 |
JPQL - JPQL 페치 조인(2) (0) | 2020.11.28 |
JPQL - JPQL 페치 조인(1) (0) | 2020.11.28 |
JPQL - 경로 표현식 (0) | 2020.11.28 |