지나공 : 지식을 나누는 공간
JPA 8 -2 : 조인, ON절, 서브쿼리, JPA 서브쿼리 한계 본문
오늘 알아볼 내용은,
- 조인의 종류
- 조인쿼리 실행하기
- ON절
- 조회 대상 필터링 (조인을 하되 조건을 걸고 부합하는 것만 조인)
- 연관관계 없는 엔티티 조인
- 서브쿼리
- (중요) JPA 서브쿼리의 한계
조인의 종류
- 내부 조인
- SELECT m FROM Member m [INNER] JOIN m.team t (inner는 생략 가능)
- 외부 조인
- SELECT m FROM Member m LEFT [OUTER] JOIN m.team t (outer는 생략 가능)
- 세타 조인 ( 연관관계가 전혀 없는것을 비교해보는조인)
- select count(m) from Member , Team t where m.username = t.name
조인 쿼리 실행하기
Team과 Member가 연관관계를 가진 상태라고 하면 아래와 같이 조인 쿼리를 실행할 수 있다.
- 내부조인
String query = "select m from Member m inner join m.team t";
List<Member> result = em.createQuery(query, Member.class).getResultList();
* 연관관계 복습
fetch type이 설정되지 않은 채로 실행된 쿼리를 보면 아래와 같다.
select m from Member m
inner join
m.team t */ select
member.id as id,
member.age as age,
member.team_id as team_id,
member.username as username
from
Member member
inner join
Team team1
on member.team_id = team1.id
select
team.id as id,
team.name as name
from
Team team
where
team.id = ?
team과 조인해서 궁극적으로는 member를 가져오려는 것인데 fetchtype이 설정되어 있지 않으면 EAGER로 먹혀서 TEAM을 조회하는 쿼리도 함께 실행된다.
- 세타조인
String query = "select m from Member m, Team t where m.username=t.name";
List<Member> result = em.createQuery(query, Member.class).getResultList();
세타조인 실행 시 cross join이 실행된다.
ON 절
ON절은 JPA 2.1부터 지원된다.
- 조인 대상 필터링
- 연관관계 없는 엔티티 외부 조인 <- 꽤 중요한 변경 사항
조인 대상 필터링이란,
예를 들어 회원과 팀을 조인할 때 팀 이름이 A인 팀만 조인하는 경우가 해당된다.
JPQL
SELECT m, t FROM Meber m LEFT JOIN m.team t on t.name = 'A'
SQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team ON m.TEAM_ID = t.id and t.name = 'A'
팀과 멤버 조인해서 팀의 멤버를 가져오려는데 on을 통해서 join 안에서도 조건을 걸 수 있다. 난 t의 name이 'A'인 애만 조인을 하겠다는 의미.
실행된 쿼리를 보면 본래의 연관관계 조인을 위한 등식은 있고 거기에 다른 조건이 추가된 쿼리가 실행된다.
연관관계 없는 엔티티 외부 조인이란,
예를 들어 회원의 이름과 팀의 이름이 같은 대상을 외부 조인하는 경우가 해당된다.
둘이 아무 연관관계가 없더라도.
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
실행된 쿼리를 보면 애초에 연관관계가 없는 엔티티였어서 id 관련 등식을 통한 조건이 사라진다. 연관관계 아니니까 그거 빼고 새로 준 조건에 대해서만 조인문이 생성되어있다.
서브쿼리 들어가기
- 나이가 평균보다 많은 회원 조회
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
서브쿼리에서 지원하는 함수
- [NOT] EXISTS (서브쿼리) : 서브쿼리에 결과가 존재하면 참
- {ALL | ANY | SOME} (서브쿼리) : ALL은 모두 만족할 때, ANY와 SOME은 조건을 하나라도 만족할 때 참
- [NOT] IN (서브쿼리) : 서브쿼리 결과 중 하나라도 같은 값이 있으면 참
예시)
- 팀 A 소속의 회원들
select m from Member m
where exists (select t from m.team t where t.name = '팀A')
- 전체 상품 각각의 재고보다 주문량이 많은 주문들
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)
JPA 서브쿼리 한계 (중요)
- JPA는 where, having 절에서만 서브 쿼리 사용 가능
- select 절에서도 서브쿼리 사용 가능(하이버네이트에서 지원)
- from 절의 서브쿼리(인라인뷰)는 현재 JPQL에서 불가능하므로 조인으로 풀 수 있다면 조인으로 해결
JPA 표준 스펙에서는 WHERE, HAVING 절에서만 서브쿼리 사용이 가능하다. 하지만 우리는 보통 구현체로 하이버네이트를 사용한다. 하이버네이트 경우에는 SELECT절에서도 서브쿼리를 사용할 수 있다.
아래 코드를 보면 select 절에서 서브쿼리를 사용하고 그걸 avgAge라는 이름으로 select 하고 있다.
select (select avg(m1.age) from Member m1) as avgAge from Member m
join Team t on m.username = t.name
그리고 인라인뷰도 현재 JPQL에서 불가능하다. 해결 방안은 아래와 같다.
- 정 안되면 조인 절로 해결해보자
- 이것도 안되면 native query로 해보자
- 이것도 안되면 쿼리를 두 번 날리는 수 밖에.
'Tech > JPA' 카테고리의 다른 글
JPA Auditing 사용하기, BaseEntity로 생성일, 수정일 자동화 (0) | 2021.02.16 |
---|---|
JPA 8 -3 : JPQL 타입 표현과 기타식, JPQL 기본 함수 (0) | 2020.12.20 |
JPA 8 -1 : 프로젝션, 페이징 (0) | 2020.12.13 |
JPA 8 : JPQL 기본 문법과 쿼리 API (0) | 2020.12.09 |
JPA 7-2 : 값 타입 컬렉션 (0) | 2020.12.07 |