github 전체코드 주소(branch: JPQL/path)
Student.java
package hellojpa;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Data
public class Student {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private int age;
@Setter(AccessLevel.PROTECTED)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CLUB_ID")
Club club;
@OneToMany(mappedBy = "student")
List<SubjectOfStudent> mySubjects = new ArrayList<>();
void changeClub(Club club) {
this.setClub(club);
club.getStudents().add(this);
}
}
Subject.java
package hellojpa;
import lombok.Data;
import javax.persistence.*;
@Entity
@Data
public class Subject {
@Id @GeneratedValue
private Long id;
private String name;
private String professor;
}
SubjectOfStudent.java
package hellojpa;
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
public class SubjectOfStudent {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "subject_id")
Subject subject;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "student_id")
Student student;
}
필드종류
select s.name from Student s # 상태필드
join s.club c # 단일값 연관필드
join s.mySubjects sub # 컬렉션 값 연관필드
위 JPQL을 보면 .을 찍어 객체그래프를 탐색하고 있다.
이렇게 .으로 객체그래프를 탐색하는 것을 경로표현식이라고 한다.
상태필드(state field): 단순히 값을 저장하기 위한 필드
연관필드(association field): 연관관계를 위한 필드
- 단일값 연관필드
대상이 엔티티이다.(ex: s.club)
사용되는 Annotation: @MayToOne, @OneToOne - 컬렉션값 연관필드
대상이 컬렉션이다.(ex: s.mySubjects)
사용되는 Annotation: @OneToMany, @ManyToMany
종류에 따른 경로 표현식 특징
상태필드 | 단일값 연관필드 | 컬렉션값 연관필드 | |
추가적인 탐색 가능 여부 | X | O | X |
묵시적 내부조인(inner join) 발생 여부 |
X | O | O |
명시적 조인과 묵시적 조인
# 명시적 조인 JPQL
select sub from Student s inner join s.mySubjects sub
# 실제 SQL
select sub.id, sub.student_id, sub.subject_id from Student s
inner join SubjectOfStudent sub on s.MEMBER_ID=sub.student_id
# 묵시적 조인 JPQL
select s.mySubjects from Student s
# 실제 SQL
select sub.id, sub.student_id, sub.subject_id from Student s
inner join SubjectOfStudent sub on s.MEMBER_ID=sub.student_id
명시적 조인: join 키워드 직접 사용
묵시적 조인: 경로 표현식에 의해 묵시적으로 실제 SQL에 내부조인 발생
두 개의 조인 모두 결과적으로 생성되는 SQL은 같다.
# 명시적 조인 JPQL - 정상작동
select sub.subject from Student s inner join s.mySubjects sub
# 묵시적 조인 JPQL - 에러발생
select s.mySubjects.subject from Student s
컬렉션값 연관필드로부터 속성을 얻는 것은 불가능하다.
만약에 얻어와야 한다면 명시적 조인을 통해 얻어온 별칭으로 탐색이 가능하다.
경로탐색을 사용한 묵시적 조인 시 주의사항
- 묵시적 조인은 항상 내부조인을 발생시킨다.
- 컬렉션에서는 추가적인 경로 탐색이 불가능하고,
추가적인 경로 탐색을 위해서는 명시적 조인을 통해 얻어온 별칭이 필요하다. - 묵시적 조인은 SQL의 FROM(JOIN) 절에 영향을 줄 수 있으며,
이로 인해 나도 모르게 수많은 조인을 부를 수 있다. - 묵시적 조인을 사용하면 JOIN 쿼리의 여부를 한눈에 파악하기 어렵다.
가급적 묵시적 조인 대신에 명시적 조인을 사용하는 것이 좋다.
'JPA > JPQL' 카테고리의 다른 글
페치조인 특징과 한계 (0) | 2021.10.12 |
---|---|
N+1과 fetch join (0) | 2021.10.02 |
JPQL 함수 (0) | 2021.09.25 |
JPQL에서의 Enum과 조건식 (0) | 2021.09.17 |
서브쿼리 (0) | 2021.09.16 |