* 경로표현식이란?
.(점)을 찍어 객체 그래프를 탐색하는 것
상태필드, 단일 값, 컬렉션 값 3가지의 경로 표현식이 있다.
select m.username //상태필드
from Member m
join m.team t //단일 값 연관필드
join m.orders o //컬렉션 값 연관필드
where t.name = '팀A';
상태필드 | 연관 엔티티 | 연관 컬렉션 | |
SELECT 문법 | m.username | m.team | t.members |
리턴타입 | String | Team | Collection |
추가 경로조회 | 불가능 | 가능 | 불가능 |
특징 | 단순한 값 | 엔티티 1개 | 엔티티 여러개 |
* 쿼리 비교
1. 상태 필드 경로 검색
//JPQL
SELECT m.username, m.age FROM Member m
//SQL
SELECT m.username, m.age FROM Member m
2. 단일 값 연관 엔티티 경로 검색
//JPQL
SELECT o.member from Order o
//SQL
SELECT m.* from Order o inner join Mmeber m on o.member_id = m.id
- 단일 값 연관 필드
@ManyToOne, @OneToOne, 대상이 엔티티(ex: m.team)
연관 엔티티 조회는 묵시적 내부조인(inner join)이 발생하며 탐색을 계속 할 수 있다.
연관 엔티티의 필드 조회는 크로스조인(cross join)이 발생한다.
...
@Entity
public class Member {
...
@ManyToOne(fetch = FetchType=LAZY)
@JoinColumn(name = "team_id")
private Team team;
...
}
* 연관 엔티티 조회
String query = "SELECT m.team FROM Member m";
List<Team> result = em.createQuery(query, Team.class)
.getResultList();
for(Team s : result) {
System.out.println(s);
}
...
Hibernate:
/* SELECT
m.team
FROM
Member m */ select
team1_.id as id1_5_,
team1_.name as name2_5_
from
Member member0_
inner join
Team team1_
on member0_.TEAM_ID=team1_.id
Team{id=1, name='팀A'}
Member가 N이므로 연관관계 주인이고 @ManyToOne이므로 연관필드를 조회할 수 있다.
* 연관 엔티티 필드 조회
String query = "SELECT m.team.name FROM Member m";
List<String> result = em.createQuery(query, String.class)
.getResultList();
for(String s : result) {
System.out.println(s);
}
...
Hibernate:
/* SELECT
m.team.name
FROM
Member m */ select
team1_.name as col_0_0_
from
Member member0_ cross
join
Team team1_
where
member0_.TEAM_ID=team1_.id
팀A
- 컬렉션 값 연관 필드
@OneToMany, @ManyToMany, 대상이 컬렉션(ex: m.orders)
묵시적 내부조인(inner join)이 발생하며 탐색을 계속 할 수 없다.
(FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색이 가능하다.)
* 컬렉션 값 연관 조회
String query3 = "SELECT t.members FROM Team t";
List<Member> result3 = em.createQuery(query3, Member.class)
.getResultList();
for(Member s : result3) {
System.out.println(s);
}
연관 리스트 출력은 Member로 받을 수 없다. 리턴형은 Collection으로 받는다.
String query3 = "SELECT t.members FROM Team t";
List<Collection> result3 = em.createQuery(query3, Collection.class)
.getResultList();
for(Object s : result3) {
System.out.println(s);
}
...
Hibernate:
/* SELECT
t.members
FROM
Team t */ select
members1_.id as id1_3_,
members1_.age as age2_3_,
members1_.city as city3_3_,
members1_.street as street4_3_,
members1_.zipcode as zipcode5_3_,
members1_.name as name6_3_,
members1_.TEAM_ID as TEAM_ID9_3_,
members1_.endDate as endDate7_3_,
members1_.startDate as startDat8_3_
from
Team team0_
inner join
Member members1_
on team0_.id=members1_.TEAM_ID
Member{id=2, name='null', age=0, team=Team{id=1, name='팀A'}}
단순한 컬렉션 값 연관 엔티티 조회에서는 필드를 조회할 수 없다. 한계를 극복하기 위해서는 명시적 조인을 사용해야 한다.
* FROM 절에서 명시적 조인
String query4 = "SELECT m FROM Team t JOIN t.members m";
List<Member> result4 = em.createQuery(query4, Member.class)
.getResultList();
for(Member s : result4) {
System.out.println(s);
}
.......
Hibernate:
/* SELECT
m
FROM
Team t
JOIN
t.members m */ select
members1_.id as id1_3_,
members1_.age as age2_3_,
members1_.city as city3_3_,
members1_.street as street4_3_,
members1_.zipcode as zipcode5_3_,
members1_.name as name6_3_,
members1_.TEAM_ID as TEAM_ID9_3_,
members1_.endDate as endDate7_3_,
members1_.startDate as startDat8_3_
from
Team team0_
inner join
Member members1_
on team0_.id=members1_.TEAM_ID
FROM절에서 명시적 조인을 하면, 컬렉션 값 연관 엔티티의 필드를 조회할 수 있다.
실무에서는 묵시적 조인을 사용하지 않고, 명시적 조인을 사용한다.
명시적 조인 : join 키워드 직접 사용
SELECT t FROM Member m join m.team t
묵시적 조인 : 경로 표현식에 의해 묵시적으로 SQL 조인 발생(내부 조인만 가능)
SELECT m.team FROM Member m
*예제
select o.member.team from Order o
성공하지만 엄청난 내부조인이 발생한다.
select t.members from Team
성공하는데, 컬렉션 값이므로 더이상 탐색이 불가능하다.
select t.members.username from Team t
컬렉션 값이므로 t.members에서 더이상 탐색할 수 없다. 탐색하려면 명시적 조인을 사용해야 한다.
select m.username from Team t join t.members m
성공하는데, 컬렉션 값의 필드를 조회하기 위해서 명시적 조인을 사용했다.
*정리
경로 탐색은 항상 묵시적 조인을 사용하여 내부조인을 발생시킨다. 그래서 JOIN 절에 영향을 준다.
묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어렵다. 묵시적 조인 대신에 명시적 조인을 사용한다.
'학습 > DB' 카테고리의 다른 글
조인 (0) | 2021.10.06 |
---|---|
프로젝션(엔티티, 임베디드, 스칼라타입) (0) | 2021.10.06 |
JPA에서 Dirty Check 방지하기 (0) | 2021.10.02 |
영속성 컨텍스트로 발생하는 이슈 (0) | 2021.10.01 |
Converter 사용하기 (0) | 2021.09.30 |