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 |
댓글