본문 바로가기

JPA

2월 16일 JPA JPQL 페치조인의 특징과 한계

JPQL 페치조인의 특징과 한계

-페치조인 대상에는 별칭을 줄 수 없다.

//join fetch t.team as t 이런거 하면 안됨 , 하이버네이트는 가능하지만 가급적 사용하지말것

-둘 이상의 컬렉션을 페치조인 할 수 없다.

-컬렉션을 페치조인하면 페이징 API를 사용할 수 없다.

ㄴ일대일 , 다대일 같은 단일값 연관 필드들은 페치조인해도 페이징 가능

ㄴ하이버네이트는 경고 로그를 남기고 메모리에서 페이징(매우 위험)

 

fetch조인은 엔티티와 연관된것 모든것을 끌고 오겟다는것

이때 추가적인 엔티티들에 영향을 주지 않는게 원칙이다.

그 이유는 전부가져오는것은 일부만 조작할 경우가 있을수 있기 떄문

 

객체 그래프는 기본적으로 데이터를 전부 조회해야한다.

필요 없는걸 걸러가면서 조회할 수는 없다.

이런 경우가 없는건 아니지만 그 경우에는 fetch가 아니라

처음부터 필요한것만 조회해야한다.

 

JPA는 기본적으로 객체그래프 모두 가져오는것을 원칙으로 한다.

 

둘 이상의 컬렉션의 경우 데이터가 생각치못한 수준으로 늘어나서 사용하면 안된다.

컬렉션의 경우 하나면 fetch join 할것

 

데이터 뻐우티기에 경우 페이징이 되지않는다.

안되는 이유는

팀 A가 회원1 , 회원2를 가진경우 (데이터 뻥튀기로 늘어남)

1개 값만 가져와 페이징하면

팀A - 회원1 까지만 가져와 마치 데이터가 1개뿐인것 처럼 보여지기 때문이다.

그래서 JPA가 데이터를 일단 전부 가져와버리기 떄문에 문제 발생

 

일대다 를 다대일로 만들거나 fetchJoin을 아예 빼거나

그런데 fetch를 빼면 지연로딩을 여러번 쓰기때문이 효율이 나질 않는다.

 

fetch를 뺀경우의 해결책으로

@BatchSize(size = 100)

@OneToMany(mappedBy = "team")

private List<Member> members = new ArrayList<>();

해당 필드에 BatchSize 옵션을 주고

 

select t from Team t; 를 하게되면 

객체그래프로 찾을 경우 트리구조로 계속 찾게되는데

 

List<Team> result = em.createQuery(query,Team.class)

.setFirstResult(0)

.setMaxResult(2)

.getResultList();

 

결과값 2개를 얻고

그 결과값의 객체 그래프탐색을 할 경우

BatchSize에 설정해둔 만큼 IN절에 엔티티의 PK값을 넣어 쿼리를 최소화 한다.

이또한 N+1이긴 하다.

 

*해결책*

엔티티의 N+1문제는 fatch Join으로 해결

컬렉션의 N+1문제는 BatchSize로 해결

 

BatchSize는 글로벌로 설정할 수 있는데

JPA설정(Persistence)에서

<property name = "hibernate.default_batch_fetch_size" value="100"/>

으로 글로벌로 설정이 가능하다.

 

연관된 엔티티들은 SQL 한번으로 조회 - 성능 최적화

엔티티에 직접 적용하는 글로벌 로딩전략보다 우수함

 

OneToMant(fetch = FetchType.LAZY) // 글로벌 로딩 전략

실무에서 글로벌 로딩전략은 모두 지연로딩으로 설정한다.

최적화가 필요한 곳은 페치조인 적용

 

N+1은 대게 fetchJoin 으로 해결가능

 

페치조인 정리

-모든 것을 페치조인으로 해결할 수는 없음

-페치조인은 객체 그래프를 유지할때 사용하면 효과적

-여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야한다면

페치조인보다 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는것이 효과적이다.