JPQL에는 경로 표현식이라는 개념이 있다.
경로표현식이란? .(점)을 찍어서 객체 그래프를 탐색하는 것이다.
예를 들면 아래와 같은 개념으로 이해하면 된다.
경로 표현식에는 두 개념이 있다.
상태 필드와 연관 필드인데,
상태 필드(state field)는 단순히 값을 저장하기 위한 필드이다.
예를 들면 m.username 과 같이 SELECT 문에서 사용하는 것처럼..
연관 필드(association field)는 연관관계를 위한 필드인데,
단일 값 연관 필드가 있고, 컬렉션 값 연관 필드가 있다
(1) 단일 값 연관 필드 :
- @ManyToOne, @OneToOne, 대상이 엔티티(ex : m.team)
(2) 컬렉션 값 연관 필드 :
- @OneToMany, @ManyToMany, 대상이 컬렉션(ex : m.orders)
상태 필드와 연관 경로의 특징이 있는데
① 상태 필드는 탐색을 하지 않는다. 그냥 상태 필드 자체로 하나의 값이기 때문이다. ex) m.username
jpql = "select m.username From Member m";
② 단일 값 연관 경로는 묵시적 내부 조인이 발생하여 탐색을 실행한다.
jpql = "select m.team From Member m";
List<Team> resultList = em.createQuery(jpql, Team.class)
.getResultList();
for(Team s : resultList) {
System.out.println("s = " + s);
}
이런 케이스는 되도록 지양하는 게 좋다.
성능 튜닝에 아주 지대한 영향을 주기 때문이다.
JPQL과 SQL의 내용이 달라지기 때문이다.
아래 실행 결과를 확인할 수 있다.
/* JPQL 내용 */
/*
select
m.team
From
Member m */
/* SQL 실행 */
select
team1_.id as id1_3_,
team1_.name as name2_3_
from
Member member0_
inner join
Team team1_
on member0_.TEAM_ID=team1_.id
실제로 JPQL은 단순히 Member에 있는 team을 조회하는 것일 뿐인데, 묵시적으로 조인이 발생하게 된다.
③ 컬렉션 값 연관 경로는 묵시적 내부 조인이 발생하나, 컬렉션 값 연관 경로는 탐색을 못한다.
jpql = "select t.members From Team t";
Collection result = em.createQuery(jpql, Collection.class).getResultList();
for(Object o : result) {
System.out.println("o = " + o);
}
jpql = "select t.members From team t";
List<Collection> result = em.createQuery(jpql, Collection.class).getResultList();
System.out.println("result = " + result);
OneToMany 관계인데, 쿼리가 너무 복잡해진다.
이렇게 될 경우, 사용하지 않을 것을 권장한다.
t.members.size 를 조회하면 아래와 같이 나온다.
jpql = "select t.members.size From Team t";
Integer result = em.createQuery(jpql, Integer.class).getSingleResult();
System.out.println("result = " + result);
t.members.size 는 Integer 타입이다.
em.createQuery(jpql, Integer.class).getSingleResult(); 로 결과를 받아야 한다.
위처럼 size외에는 탐색이 불가능 한데,
이럴 경우 명시적으로 조인을 해주면 된다.
jpql = "select m From Team t join t.members m";
List<Member> result = em.createQuery(jpql, Member.class).getResultList();
for (Member m : result) {
System.out.println("m = " + m);
}
위처럼 JPQL을 명시적으로 적으면 쿼리도 명시적으로 나오게 된다.
FROM 절에서 명시적 조인을 통해서 별칭을 얻으면 별칭을 통해서 탐색이 가능하다.
상태 필드로 경로 탐색의 비교
JPQL : select m.username, m.age from Member m
SQL : select m.username, m.age from Member m
단일 값 연관 경로 탐색
JPQL : o.member
String jpql = "select o.member from Order o";
SQL : inner join Member m on o.member_id = m.id
select m.*
from Orders o
inner join Member m on o.member_id = m.id
명시적 조인과 묵시적 조인 차이
- 명시적 조인 : join 키워드를 직접 사용한다.
: select m from Member m join m.team t
- 묵시적 조인 : 경로 표현식에 의해 묵시적으로 SQL 조인이 발생하는데 내부 조인만 가능하다
: select m.team from Member m
경로 표현식의 예제)
1) select o.member.team from Order o → 성공
2) select t.members from Team → 성공
3) select t.members.username from Team t → 실패
4) select m.username From Team t join t.members.m → 성공
경로 탐색을 사용한 묵시적 조인 시 주의사항
1) 항상 내부 조인
2) 컬렉션은 경로 탐색의 끝, 명시적 조인을 통해 별칭을 얻어야 한다.
3) 경로 탐색은 주로 SELECT, WHERE 절에서 사용하지만, 묵시적 조인으로 인해 SQL의 FROM(JOIN) 절에 영향을 준다.
강의를 통해 얻은 실무 조언
1) 가급적 묵시적 조인 대신에 명시적 조인을 사용할 것
2) 조인은 SQL 튜닝에 중요 포인트
3) 묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어렵다.
'자바 > JPA' 카테고리의 다른 글
[JPA/Java] JPQL 패치 조인의 한계 - 글로벌 배치 패치 사이즈, @BatchSize (0) | 2023.05.11 |
---|---|
[JPA/Java] JPQL fetch join (패치 조인) (2) | 2023.05.10 |
[JPA/Java] JPQL 기본함수 및 사용자 정의 함수 호출 (0) | 2023.05.10 |
[JPA/Java] JPQL CASE 문, COALESCE, NULLIF (0) | 2023.05.09 |
[JPA/Java] JPQL 타입 표현과 기타식 (0) | 2023.05.09 |
댓글