3 분 소요

📌 Topic

  • 💡 JPQL 경로 표현식

⚡ 01. JPQL 경로 표현식

01-1 경로 표현식

select m.username     -- 상태 필드
from Member m
    join m.team t     -- 단일 값 연관 필드
    join m.orders o   -- 컬렉션 값 연관 필드
where t.name = '팀A'

경로 표현식이란 .(점)을 찍어 탐색하는 것을 의미한다. 우선 위 SQL을 보는 것만으로는 이해가 쉽게 되지 않으니 다음 경로 표현식 용어 정리를 살펴보자.

  • 상태 필드
  • 단일 값 연관 필드
  • 컬렉션 값 연관 필드

01-2. 경로 표현식 용어 정리

// Entity : Member
// target : 단일 값 연관 필드
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private JpqlTeam team;
// Entity : Member
// target : 컬렉션 값 연관 필드
@OneToMany(mappedBy = "member")
private List<Order> order = new ArrayList<>();
  • 상태 필드(state field)
    • 단순히 값을 저장하기 위한 필드 (ex : m.username)
  • 연관 필드(association field)
    • 연관관계를 위한 필드
      • 단일 값 연관 필드
        • @ManyToOne @OneToOne
        • 타겟 대상이 엔티티인 경우
      • 컬렉션 값 연관 필드
        • @OneToMany, ManyToMany
        • 타겟 대상이 컬렉션인 경우

01-3. 경로 표현식 특징

// m.username에 다음에 '.' 을 찍어서 탐색이 불가능 하다.
// 즉, 상태 필드는 경로 탐색의 끝이라 할 수 있다.
String query = "select m.username.컬럼or엔티티 From JpqlMember m";

List<String> resultList = em.createQuery(query, String.class)
                             .getResultList();
  • 상태 필드(state field)
    • 경로 탐색의 끝이다.
    • 그래프 탐색은 불가능하다.
    • m.username 다음에 ‘.’을 찍어서 접근이 불가능하기 때문이다.
// 단일 값 연관 필드
// @ManyToOne, @OneToOne 참조를 하면 탐색이 가능하고
// -> (중요) 묵시적 내부 조인이 발생한다
String query = "select m.team From JpqlMember m";

List<JpqlTeam> resultList = em.createQuery(query, JpqlTeam.class)
                               .getResultList();

for (JpqlTeam team : resultList) {
    System.out.println("team = " + team);
}
// 해당 JPQL을 실행한 결과, 묵시적 내부 조인(INNER JOIN) 발생
// m.team을 통해 team만 가져오라고 했는데, Member와 Team을 조인해서 가져온다.
Hibernate:
    /* select
        m.team
    From
        JpqlMember m */ select
            jpqlteam1_.TEAM_ID as team_id1_13_,
            jpqlteam1_.name as name2_13_
        from
            JPQL_MEMBER jpqlmember0_
        inner join
            JPQL_TEAM jpqlteam1_
                on jpqlmember0_.TEAM_ID=jpqlteam1_.TEAM_ID
  • 단일 값 연관 경로
    • 묵시적 내부 조인(INNER JOIN) 발생
    • 그래프 탐색이 가능하다.
    • m.team 다음에 ‘.’을 찍어서 다른 상태 필드나 참조 변수에 접근이 가능.
    • 묵시적 내부 조인이 발생 하도록 설계를 하면 안된다.
      • 묵시적 내부 조인은 쿼리 튜닝 역시 쉽지가 않다.
// 컬렉션 값 연관 경로
String query = "select t.memberList From JpqlTeam t";

Collection resultList = em.createQuery(query, Collection.class)
                            .getResultList();
  • 컬렉션 값 연관 경로
    • 묵시적 내부 조인 발생
    • 그래프 탐색은 불가능하다.
    • FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색 가능.

💡 묵시적 조인은 사용하지 말고, 명시적 조인을 사용해야 한다.

01-4. 단일 값 연관 경로 탐색

// JPQL
// 묵시적 내부 조인 발생, 상당히 위험하다
select o.member from Order o
-- SQL
-- 위 JPQL 쿼리와 아래 SQL문이 동일하다 봐도 무방
select m.*
from Orders o
inner join Member m on o.member_id = m.id

01-5. 명시적 조인, 묵시적 조인

실무에서는 웬만하면 명시적 조인을 사용하여 데이터를 조회 해야 한다

  • 명시적 조인
    • join 키워드를 직접 사용한다.
  • 묵시적 조인
    • 경로 표현식에 의해 묵시적으로 SQL 조인 발생(내부 조인만 가능)
    • select m.team from Member m

01-6. 경로 표현식 - 예제

// 성공 여부 - o
select o.memeber.team
from Order o
  • 조회는 가능하다.
  • 하지만 묵시적 내부 조인이 3번이나 발생한다.
  • 내부적으로 어떤 쿼리가 날라가는지 파악이 힘듬.
// 성공 여부 - o
select t.memberList from Team
  • 컬렉션에서 끝이 났기 때문에 가능하다.
  • Member(1) : team(N) 관계로 봐주면 된다.
// 성공 여부 - x~
select t.memberList.username from Team t
  • 컬렉션 값 연관 관계는 그래프 탐색이 불가능하다.
  • 탐색을 하려면 명시적 조인을 사용하여 값을 가져와야 한다.
// 성공 여부 - o
select m.username from Team t join t.memberList m
  • 명시적 조인을 사용하여 별칭인 ‘m’ 을 통해 값을 출력할 수 있다.
  • 위에서 불가능 했던 부분이다.

01-7. 경로 탐색을 사용한 묵시적 조인 시 주의사항

  • 항상 내부 조인을 수행한다.
    • 항상 내부 조인을 하기 때문에 반드시 유의하여 사용을 해야 한다.
  • 컬렉션은 경로 탐색의 끝, 명시적 조인을 통해 별칭을 얻어야 한다.

경로 탐색은 주로 SELECT, WHERE 절에서 사용하지만 묵시적 조인으로 인해
SQL의 FROM(JOIN) 절에 영향을 줄 수 있다

⚡ 02. 실무 조언

  • 항상 묵시적 조인 대신에 명시적 조인을 사용해야 한다.
  • 조인은 SQL 튜닝에 있어 상당히 중요한 포인트다.
  • 묵시적 조인은 조인이 일어나는 상황을 한 눈에 파악하기가 어렵다.
    • 즉, 명시적 조인을 사용하자.

참고 자료

댓글남기기