DevBoi

[JPA] 패치조인 본문

Develop/[JPA]

[JPA] 패치조인

HiSmith 2022. 3. 11. 20:01
반응형

SQL 조인 기능이 아니다.

JPQL에서 성능 최적화를 위해서 사용된다.

연관된 엔티티나 컬렉션을 SQL 한번에 함께 조회하는 기능이다.

join fetch 명령어를 사용한다.

 

이런식으로 join fetch를 하게 되면 

내부적으로 inner join이 발생되어서, 해당 쿼리는 한번만으로 조회를 전부 해온다.

Lazy fetch type이여도, 프록시 엔티티가아닌, 실제 엔티티 값으로 가져온다. (Team도)

 

컬렉션 타입을 공부해보자

 

 

이런식으로 팀의 member들에 대한 리스트를 가지고있다고 가정해보자

팀은 총 2개인데, 팀을 가진 member가 3인 경우?

 

총 3개가 노출이된다.

 

응? 팀을 select 했는데, 왜 3개가 되지

이런경우 사용되는게 바로, distinct이다.

Team 하위 멤버가 2개이면, 총 2개의 Result를 당연히 내빝는다.

이건 distinct를 써도 내부조인으로 걸러질수없다.,

 

하지만 jpa 의 distinct가 이를 해겷한다. 해당 jpa의 distinct는 Team의 객체에 대한 내용이 중복이면, 객체를 받아올때

어플리케이션 시점에서 중복을 제거해주는 역할을 해준다.

 

그래서 jpa 의. distinct는 특별하다

2개를 추가로 넣은 경우,

distinct사용하면 4개 return 하는걸 볼수있다.

사용안하면, 6개였다

 

같은 식별자의 엔티티의 중복을 제거해주는 역할을 해주는 좋은 중요한 녀석이라고 이해하면 된다.

 

 

 

그러면 패치 조인과 일반조인의 차이는 무엇일꼬??

패치조인은, 사용할때만 연관된 엔티티를 가져온다. 즉 즉시로딩이 발생한다고 보면된다.

한방쿼리로, 내가 필요한 데이터를 함꼐 가져오는 기능이라고 보면 된다.

패치 조인은 연관된 엔티티를 함께 조인한다.

-> 패치 조인은, 엔티티에 설정된 글로벌 로딩 전략보다 우선권을 가진다.

앵간한 jpa의 N+1 성능이슈는 fetjoin으로 해결이 가능하다.

 

패치 조인의 한계

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

-하이버네이트는 가능하지만 가급적 사용하지 않아야 한다.

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

-컬렉션 패치조인을 하면, 페이징 api를 쓸수없다.

-> 컬렉션 패치조인을 하게 되면, 데이터가 뻥튀기 되게 되고, 해당 부분에서 페이징을 사용하게 되면, 데이터가 잘려버릴수있는 결과를 초래한다.

-> 하이버 네이트에서는 메모리에 올리고 해당 메모리에서 페이징을 하기 때문에 위험하다.

 

 

jpa N+1에 대한  문제점 해결 방안

 

 BatchSize를 쓴다

fetchjoin을 쓴다

Lazy 타입으로 로딩한다.

 

fetchjoin은 중요해서, 한번 씩 더 정리해보자

fetchjoin은 글로벌 로딩 전략보다 우선순위에 있고,

해당 조인을 사용하게 되면 즉시 로딩으로 엔티티를 가져오게 된다.

글로벌 로딩 전략이 지연로딩이여도, 필요한 시점에 즉시로딩으로 값을 가져와서 사용할수 있어서 좋고성능상으로도 좋다

 

일반 join과 fetch join의 차이는 무엇일까?

-일반 join : 연관 엔티티에 조인을 걸어도 실제 쿼리에서 셀렉트 하는 엔티티는 오직 jpql에서 조회하는 주체가 되는 

엔티티만 조회하여 영속화를 한다.

조회의 주체가 되는 엔티티만 셀렉트해서 영속화 하기 때문에 데이터는 필요하지 않지만, 연관 엔티티가 검색 조건에 필요한 경우

필터링되서 받아야 하는 경우에 주로 사용이 되고

-fetch join : 조회의 주체가 되는 엔티티 이외에 패치 조인이 걸린 연관 엔티티도 함께 select 해서 영속화를 한다.

이미 영속화 되어있기 때문에 N+1 문제가 해결이 된다.

 

 

예를 들면, 이런식으로 Member를 조회하고, team에 대한 정보를 변경한다면,

해당 update 쿼리가 나가게 된다.

그런데,

해당 Inner join으로 가져오게 되면 우선 해당 하위의 team은 프록시 객체 이기 때문에 해당 정보를 get하는 순간에,

한번더 select 하는 쿼리가 나가게 된다. 글로벌 로딩 전략이 eager이라면, inner join순간에 team에 대한 로딩 까지 한번에 한다

 

 

fetch join은

Hibernate: select member0_.member_id as member_i1_12_0_, team1_.team_id as team_id1_19_1_, member0_.locker_id as locker_i5_12_0_, member0_.member_gender as member_g2_12_0_, member0_.member_name as member_n3_12_0_, member0_.member_pw as member_p4_12_0_, member0_.team_id as team_id6_12_0_, team1_.major_team_id as major_te3_19_1_, team1_.team_name as team_nam2_19_1_ from member member0_ inner join team team1_ on member0_.team_id=team1_.team_id

 

 

inner join은 

Hibernate: select member0_.member_id as member_i1_12_, member0_.locker_id as locker_i5_12_, member0_.member_gender as member_g2_12_, member0_.member_name as member_n3_12_, member0_.member_pw as member_p4_12_, member0_.team_id as team_id6_12_ from member member0_ inner join team team1_ on member0_.team_id=team1_.team_id

 

쉽게말하면

 

inner join은 조인된 테이블에 존재하는 대상을 대상 엔티티 정보만을 select로 반환 -> 영속성에 보관

fetchjoin은 조인된 테이블에 존재하는 대상과 조인하는 테이블 의 정보를 같이 select로 반환 -> 영속성에 보관한다.

 

 

 

 

 

이외 엔티티를 파라미터로 전달할때, pk값을 파라미터로 전달할때가 실행되는 sql에서는 같이 쓰이는것을 알수있다.

 

반응형

'Develop > [JPA]' 카테고리의 다른 글

[JPA] Bulk 연산  (0) 2022.03.11
[JPA] Named 쿼리  (0) 2022.03.11
[JPA] 경로 표현식  (0) 2022.03.11
[jpa] 페이징 & 조인 & 쿼리 간략  (0) 2022.03.11
[JPQL] JPQL 시작하기  (0) 2022.03.10