본문 바로가기
Back-end/JPA 개념

30. JPQL - 경로 표현식과 묵시적 JOIN

by devraphy 2022. 5. 3.

0. 개요

- 이번 포스팅에서는 경로 표현식과 묵시적 JOIN에 대해서 알아보자. 

- 설명을 위해 다음 객체를 사용할 예정이다.

 

@Entity
public class Player {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private String position;
    private int age;

    @Embedded
    private Address address;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
    // Getter & Setter 생략
}

 

@Entity
public class Team {

    @Id @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    
    // Getter & Setter 생략
}

 

 

1. 경로 표현식

a) 경로 표현식이란?

- 경로 표현식은 특별하거나 새로운 기능이 아니다.

- 이는 JPQL에서 점 연산자(.)로 조회 대상의 속성을 탐색하는 것을 말한다. 

 

SELECT p.name, p.age FROM Player p;

 

- 위의 예시처럼, JPQL을 사용하고 있다면 이미 경로 표현식을 사용하고 있는 것이다.

- 다만, 경로 표현식의 종류에 따라 객체 탐색의 범위가 달라진다.   

 

- 경로 표현식에는 다음과 같은 종류가 있다.

   → 상태 필드

   → 단일 값 연관 필드

   → 컬렉션 값 연관 필드

 

 

2. 경로 표현식의 종류

a) 상태 필드

- 상태 필드는 조회 대상이 객체의 단일 속성을 의미한다.

- 즉, 객체의 경로 탐색을 한 번만 할 수 있는 대상이다.

- 상태 필드는 객체 탐색이 불가능하다.

 

- 다음 예시를 살펴보자.

SELECT p.name FROM Player p;

 

- Player 객체의 name 속성은 단순한 상태 필드다. 

- 이처럼, 단 한 번의 경로 탐색만 가능한 대상을 말한다. 

 

 

b) 단일 값 연관 필드

- 단일 값 연관 필드는 객체 내부에 존재하는 참조 객체를 의미한다.

- 즉, 한번 이상의 경로 탐색을 할 수 있으며 묵시적 JOIN이 발생하는 대상이다.

- 단일 값 연관 필드는 객체 탐색이 가능하다.

 

- 다음 예시를 살펴보자.

SELECT p.team.name FROM Player p;

 

- Player 객체는 team이라는 참조 객체를 속성으로 가진다.

- 위의 예시처럼 Player객체에서 Team 객체를 탐색할 수 있다.

- 이처럼 2번의 경로 탐색을 수행할 수 있다.

 

- @ManyToOne 또는 @OneToOne 연관 관계를 맺은 Entity가 여기에 해당한다.

- 이와 같은 조회 대상을 단일 값 연관 필드라고 부른다.

 

 

c) 컬렉션 값 연관 필드

- 컬렉션 값 연관 필드는 조회 시 컬렉션을 반환하는 대상을 의미한다.

- @OneToMany 또는 @ManyToMany 연관 관계를 맺은 대상이 여기에 해당한다.

- 조회 시 컬렉션을 반환하므로 컬렉션 객체를 통한 객체 탐색은 불가능하다. 

- 즉, 컬렉션 값 연관 필드는 객체 탐색이 불가능하다.

 

- 다음 예시를 살펴보자.

String query = "select t.players From Team t";

Collection resultList = em.createQuery(query, Collection.class).getResultList();

for (Object s : resultList) {
    System.out.println("s = " + s);
}

 

- 위의 예시에서 players는 @OneToMany의 관계를 가진다.

- players는 Team 객체와 매칭 되는 모든 Player 객체를 반환한다. 

- 즉, DB의 측면에서는 Team과 Player 테이블의 매칭 데이터를 조회해야 하므로 묵시적 JOIN이 발생한다.

 

- 다음 JPA가 발행한 쿼리문을 살펴보자.

 

 

d) 컬렉션 값 연관 필드 - 명시적 JOIN

- 컬렉션 값 연관 필드는 묵시적 JOIN이 발생하며 객체 탐색이 불가능하다고 설명하였다.

- 그러나 명시적 JOIN을 사용하면 객체 탐색을 가능하게 할 수 있다.

 

- 다음 예시를 살펴보자. 

String query = "select p.name from Team t join t.players p";

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

System.out.println("resultList = " + resultList);

 

- 위의 예시처럼 FROM 절에서 명시적 JOIN을 통해 별칭을 얻어오는 방법을 사용한다.

- 이처럼, 컬렉션 값 연관 필드에서 명시적 JOIN을 사용하면 객체 탐색이 가능하다. 

 

 

3. 묵시적 JOIN

a) 묵시적 JOIN이란?

- JPQL에는 명시되지 않은 JOIN이 JPA에 의해 번역된 쿼리에서 사용되는 것을 말한다.

- 즉, 개발자가 명시하지 않은 JOIN 쿼리가 JPA에 의해 발행되는 것을 의미한다. 

- 참고로 묵시적 JOIN은 내부 조인(= INNER JOIN)만 가능하다. 

 

 

b) 묵시적 JOIN의 예시 

- 단일 값 연관 필드가 묵시적 JOIN의 대표적인 예다.

- 단일 값 연관 필드를 조회할 때 묵시적 JOIN이 발생한다.

 

- 다음 예시를 살펴보자.

SELECT p.team FROM Player p;

 

- 위의 JPQL을 실행하면 어떤 쿼리문이 생성될지 생각해보자.

- JPA의 관점에서는 team은 Player 객체가 가진 속성이자 참조 객체에 불과하다.

- 다만, DB의 입장에서 team은 Team 테이블의 데이터를 의미한다. 

- 그러므로 다음과 같이 Inner Join이 포함된 쿼리가 작성된다.

- Player 테이블과 Team 테이블에 명시된 공통의 데이터를 가져와야 하기 때문이다. 

 

 

c) 묵시적 JOIN 해결방법

- 실무에서는 묵시적 JOIN을 사용하지 않는다. 

- 반드시 명시적 JOIN만을 사용한다. 

 

- 즉, 애초에 묵시적 JOIN이 발생하지 않도록 설계하는 것이 해결 방법이다.

- 그러므로 개발자의 의도와 다르게 묵시적 JOIN이 발생하지 않도록 설계 단계에서 이를 차단한다.

'Back-end > JPA 개념' 카테고리의 다른 글

32. JPQL - Collection Fetch Join과 Distinct  (0) 2022.05.05
31. JPQL - Fetch Join 개념  (0) 2022.05.04
29. JPQL - 사용자 정의 함수  (0) 2022.05.02
28. JPQL - case 식  (0) 2022.04.28
27. JPQL - 서브 쿼리 & 타입 표현  (0) 2022.04.27

댓글