JPA

JPQL - JPQL 기본(6)

방구석 대학생 2020. 11. 16. 19:57

"인프런 - 자바 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 에서의 타입 표현과 기타식과 같은 기능들을 알아보자.