JPQL - JPQL 기본(6)
"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
조인 ON 절
JPA 2.1 버전 부터 ON 절을 활용한 조인을 지원한다.
1. 조인 대상 필터링 : 조인할 때 미리 조인 대상을 필터링 할 수 있다.
2. 연관관계 없는 Entity 외부 조인(Hibernate 5.1 부터 지원)
: 세타 조인에서 멤버의 이름과 팀 이름이 같은지를 비교하는 것과 같이 의미상 연관관계가 없는 데이터들을 대상으로 비교를 해야할 때, 해당 Entity 들을 외부 조인 시켜줄 수 있다.
1. 조인 대상 필터링
예시 : 회원과 팀을 조인하면서, 팀 이름이 A 인 팀만 조인하고 싶은 경우 어떻게 해야할까?
JPQL : SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
SQL : SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID = t.id and t.name = 'A'
- JpaMain.java
String query = "select m from Member m left join m.team t on t.name = 'teamA'";
- Hibernate SQL
Hibernate:
/* select
m
from
Member m
left join
m.team t
on t.name = 'teamA' */ select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.TEAM_ID as team_id4_0_,
member0_.username as username3_0_
from
Member member0_
left outer join
Team team1_
on member0_.TEAM_ID=team1_.id
and ( /* join 절에 추가로 조건문이 생성된다. */
team1_.name='teamA'
)
2. 연관관계 없는 Entity 외부 조인
예시 : 회원의 이름과 팀의 이름이 같은 대상을 외부 조인하는 경우
JPQL : SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
SQL : SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name
- JpaMain.java
// 우선 회원의 이름을 member1 이 아닌 teamA 로 변경한 후 코드를 작성하자.(회원의 이름이 좀 많이 이상한것 같다.)
String query = "select m from Member m left join Team t on m.username = t.name";
- Hibernate SQL
Hibernate:
/* select
m
from
Member m
left join
Team t
on m.username = t.name */ select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.TEAM_ID as team_id4_0_,
member0_.username as username3_0_
from
Member member0_
left outer join
Team team1_
on ( /* 조인 대상 필터링과 달리 id 값은 포함되지 않았다. */
member0_.username=team1_.name
)
JPQL 서브 쿼리
서브 쿼리란 쿼리가 있을 때 그 내부에서 서브로 만들 수 있는 쿼리를 뜻한다.
예시 1 : 나이가 평균보다 많은 회원
JPQL : SELECT m from Member m where m.age > (SELECT avg(m2.age) from Member m2)
-> 보통 이와 같이 메인 쿼리와 서브 쿼리 간에 비교 대상으로 쓸 객체가 같지 않아야(서로 관련이 없어야) 성능이 잘 나온다.
예시 2 : 한 건이라도 주문한 고객
JPQL : SELECT m from Member m where (SELECT count(o) from Order o where m = o.member) > 0
-> 첫번째 예시와는 달리 두번째 예시처럼 메인 쿼리의 객체를 서브 쿼리로 가지고 오는 경우 성능이 잘 나오지 않는다.
* 서브 쿼리 지원 함수
- [NOT] EXISTS(subquery) : 서브 쿼리에 결과가 존재하면 참을 반환한다.
- {ALL | ANY | SOME} (subquery)
- ALL : 조건을 모두 만족하면 참을 반환한다.
- ANY, SOME : 서로 같은 의미로, 조건을 하나라도 만족하면 참을 반환한다.
- [NOT] IN (subquery) : 서브 쿼리의 결과 중 하나라도 같은 것이 있으면 참을 반환한다.
서브 쿼리 지원 함수 사용 예제
- 팀 A 소속인 회원 검색
JPQL : SELECT m from Member m where EXISTS (SELECT t from m.team t where t.name = '팀A')
- 전체 상품 각각의 재고보다 주문량이 많은 주문들
JPQL : SELECT o from Order o where o.orderAmount > ALL (SELECT p.stockAmount from Product p)
- 어떤 팀이든 팀에 소속된 회원
JPQL : SELECT m from Member m where m.team = ANY (SELECT t from Team t)
* 그런데 JPA 에서의 서브 쿼리는 굉장히 중요한 한계점이 존재한다.
- JPA 는 where, having 절 에서만 서브 쿼리를 사용하는 것이 가능하다.
- select 절에서도 사용 가능하지만 이것은 Hibernate 에서 지원해주는 것이다.
- 중요한 한계점으로, from 절의 서브 쿼리는 현재 JPQL 에서 불가능하다.(조인으로 풀 수 있으면 풀어서 해결한다.)
* 정 안 되겠다 싶으면 그냥 네이티브 SQL 을 쓰거나 쿼리를 2번 날려주자.
* SELECT 절에서 서브 쿼리를 사용하는 경우의 예시
- JpaMain.java
String query = ""select (select avg(m1.age) from Member m1) as avgAge from Member m
join Team t on m.username = t.name;
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
* 다음과 같이 from 절에서 서브 쿼리를 사용하면 에러가 발생한다.
- JpaMain.java
// 현재 JPA 에서는 from 절에서 서브 쿼리를 작성하는 기능을 지원하지 않고 있다.
String query_from = "select mm.age, mm.username" +
"from (select m.age, m.username from Member m) as mm";
List<Member> result_from = em.createQuery(query_from, Member.class)
.getResultList();
다음 글 에서는 JPQL 에서의 타입 표현과 기타식과 같은 기능들을 알아보자.