본문 바로가기
자바/JPA

[JPA/Java] JPQL 경로 표현식

by drCode 2023. 5. 10.
728x90
반응형

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 관계인데, 쿼리가 너무 복잡해진다.

이렇게 될 경우, 사용하지 않을 것을 권장한다.

 

members. 에서 size만 확인할 수 있고, 이 이상으로는 탐색이 불가능하다.

 

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 조회 결과

t.members.size 는 Integer 타입이다.

 

em.createQuery(jpql, Integer.class).getSingleResult(); 로 결과를 받아야 한다.

 

members. 에서 size만 확인할 수 있고, 이 이상으로는 탐색이 불가능하다.

위처럼 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을 명시적으로 적으면 쿼리도 명시적으로 나오게 된다.

 

명시적 SQL 실행 결과

FROM 절에서 명시적 조인을 통해서 별칭을 얻으면 별칭을 통해서 탐색이 가능하다.

 

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) 묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어렵다.

728x90
반응형

댓글