// 경로 표현식을 사용한 JPQL 예제
select m.username ->상태 필드
from member
join m.team t -> 단일 값 연관필드
join m.orders o -> 컬렉션 값 연관필드
where t.name = '팀A'
위의 JPQL에서 m.username, m.team, m.orders, t.name이 모두 경로 표현식을 사용한 예시이다.
경로 표현식에는 아래와 같은 용어들을 사용한다.
💡상태 필드는 단순히 값을 저장하는 필드이고, 연관 필드는 객체 사이의 연관관계를 맺기 위해 사용하는 필드다.
// 엔티티 내에서의 상태필드 & 연관 필드 예제 코드
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "name")
private String username; // 상태 필드
private Integer age; // 상태 핃드
@ManyToOne(...)
private Team team; // 연관 필드(단일 값 연관필드)
@OneToMany(...)
private List<Order> orders; // 연관 필드(컬렉션 값 연관필드)
}
위의 엔티티를 보았을때 경로 표현식을 사용한다면 3가지 경로 표현식을 사용할 수 있다.
3가지 경로 표현식을 사용해서 경로 탐색을 하려면 3가지 경로에 따라 특징이 나뉜다.
단일 값 연관 필드로 경로 탐색을 하면 SQL에서 내부조인이 일어난다.
이것을 묵시적 조인이라고 하며, 묵시적 조인은 모두 내부 조인이다. 외부 조인을 사용해야 한다면 명시적으로 JOIN 키워드를 사용해야 한다.
SELECT m FROM Member m JOIN m.team t
SELECT m FROM Member m
💡 임베디드 타입에 접근하는 것도 단일 값 연관 경로 탐색이지만, 이미 다른 엔티티에 포함되어 있는 값이므로 조인이 발생하지 않는다.
JPQL에서는 컬렉션 값에서 경로 탐색을 할 수 없다.
컬렉션 값에서 꼭 경로 탐색을 해야하는 케이스가 있다면 FROM절에서 조인을 통해 별칭을 얻어야 한다.
// 컬렉션 값에서의 연관 경로 탐색
select t.members from Team t // 성공
select t.members.username from Team t // 실패
// 별칭을 얻어 컬렉션 값에서 경로 탐색
select m.username from Team t join t.members m // 성공
💡 참고로 컬렉션은 컬렉션의 크기를 구할 수 있는 size라는 기능을 사용할 수 있다. size를 사용하면 COUNT 함수를 사용하는 SQL로 적절히 변환된다.
경로 탐색을 사용하면 묵시적 조인이 발생한다.
이로 인해 SQL에서 내부 조인이 일어날 수 있는데 이때 주의할 점이 몇개 있다.
💡 성능이 중요하다면 분석하기 쉽도록 묵시적 조인보다는 명시적 조인을 사용하는 것이 좋다.
JPQL도 SQL처럼 서브 쿼리를 지원한다. JPQL에서는 서브 쿼리에 몇가지 제약이 있다.
💡 일부 JPA 구현체는 FROM 절의 서브 쿼리도 지원한다.
// JPQL 서브 쿼리 사용 예제
// 나이가 평균보다 많은 회원을 조회
select m from Member m
where m.age > (select avg(m2.age) from Member m2)
// 한 건이라도 주문한 고객을 조회
select m from Member m
where (select count(o) from Order o where m = o.member) > 0
// 한 건이라도 주문한 고객을 컬렉션 값 연관 필드의 size 기능으로 조회
select m from Member m
where m.orders.size > 0
서브 쿼리는 아래의 함수들과 같이 사용이 가능하다.
EXISTS
select m from Member m
where exists (select m from m.team t where t.name = '팀A')
{ALL | ANY | SOME}
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from product p)
select m from Member m
where m.team = ANY (select t from Team t)
IN
select t from Team t
where t IN (select t2 From Team t2 JOIN t2.members m2 where m2.age >= 20)