JPQL 페치 조인

김혁준·2024년 6월 29일

JPA

목록 보기
11/16

페치 조인(fetch join)

실무에서 자주쓰는 개념이다
JPQL에서 성능 최적화를 위해 제공하는 기능이다
연관된 엔티티나 컬레션을 SQL 한 번에 함께 조회하는 기능이다
join fetch 명령어를 사용한다.

엔티티 페치 조인

예를 들어 회원을 조회하면서 연관된 팀도 함께 조회한다
회원 -> 팀 N:1
JPQL : select m from Member m join fetch m.team
SQL : SELECT M., T. FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID = T.ID

컬렉션 페치 조인

일대다 관계인 컬렉션 페치 조인이다.
예)

JPQL : select t from Team t join fetch t.members where t.name = "팀A"

SQL : SELECT T.*, M.* FROM TEAM T INNER JOIN MEMBER M ON T.ID = M.TEAM_ID WHERE T.NAME = '팀A'

페치 조인과 일반 조인의 차이

일반 조인은 연관된 엔티티를 함께 조회하지 않는다.
페치 조인을 사용할 때만 연관된 엔티티도 함께 조회된다. 객체 그래프를 SQL 한번에 조회하는 개념이다.
페치 조인은 일반적으로 N+1 문제를 해결할 수 있다. 일반 조인을 쓰면 코드에서 팀객체를 통해 멤버 객체들을 조회하면 멤버의 수만큼 쿼리가 나간다. 하지만 페치 조인을 쓰면 첫 팀객체 조회 쿼리부터 멤버까지 조회하므로 한번밖에 안나간다.

페치 조인의 특징과 한계

페치 조인 대상에는 별칭을 줄 수 없다
둘 이상의 컬렉션은 페치 조인 할 수 없다
컬렉션 페치 조인하면 페이징 API를 사용할 수 없다.
연관된 엔티티들을 SQL 한번으로 조회하여 성능이 최적화 된다
엔티티에 직접 적용하는 글로벌 로딩 전략보다 우선한다
실무에서 글로벌 로딩 전략은 모두 지연 로딩을 쓰고 최적화가 필요하면 페치 조인을 적용하자
모든 것을 페치 조인으로 해결할 수는 없으므로 객체 그래프를 유지할때 사용하자.
여러 테이블을 조인해서 조합하려면 일반 조인을 사용해서 필요한 데이터들만 조회해서 DTO로 반환하자

다형성 쿼리

조회 대상을 특정 자식으로 한정하는 것이다.

JPQL : select i from Item i where type(i) IN (Book, Movie)
SQL : select i from i where i.DTYPE in ('B','M')

TREAT

자바의 타입 캐스팅과 유사
상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용한다

JPQL : select i from Item i where treat(i as Book).author = 'kim'
SQL : select i.* from Item i where i.DTYPE = 'B' and i.author = 'kim'

엔티티 직접 사용

JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용한다

select count(m.id) from Member m // 엔티티의 아이디를 사용
select count(m) from Member m // 엔티티를 직접 사용
-> 둘다 같은 SQL(id 사용)을 실행한다.

Named 쿼리

미리 정의해서 이름을 부여해두고 사용하는 JPQL
정적 쿼리로 사용된다. 어노테이션, 설정파일에 정의한다
어플리케이션 로딩 시점에 쿼리를 검증할 수 있는 장점이 있다.
어노테이션 활용 예)

@Entity
@NamedQuery(
name = "Member.findByUsername",
query="select m from Member m where m.username = :username")
public class Member {}

List<Member> resultList =
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username",
"회원1")
.getResultList();

벌크 연산

쿼리 한 번으로 여러 테이블 로우를 변경한다
executeUpdate()를 사용하며 결과는 영향받은 엔티티의 수를 반환한다
UPDATE, DELETE를 지원한다
예시)

String qlString = "update Product p " +
					"set p.price = p.price * 1.1 " +
					"where p.stockAmount < :stockAmount";
                
int resultCount = em.createQuery(qlString)
					.setParameter("stockAmount", 10)
					.executeUpdate();

영속성 컨텍스트를 무시하고 DB에 직접 쿼리하므로 주의해야 한다.
헷갈리지 않으려면 벌크 연산을 먼저 실행하거나 벌크 연산 실행 후 영속성 컨텍스트를 초기화 하자

출처 : 인프런 자바 ORM 표준 JPA 프로그래밍 - 기본편

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

0개의 댓글