spring/JPA

[JPA] 객체 지향 쿼리 언어 JPQL 6 (fetch join의 한계, 다형성 쿼리)

대기업 가고 싶은 공돌이 2024. 8. 6. 01:00

페치 조인의 한계

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

  • 하이버네이트는 가능하나 가급적 사용하지 말 것
  • 별칭을 주고 select t from Team t join fetch t.members m where m.age > 10;
    과 같이 조회하게 되면 멤버의 전체를 가져오는 것이 아닌 멤버의 일부만 가져오게 된다.
    그러나 JPA는 fetch를 사용하면 모든 객체를 가져오는 것을 기본으로 설계가 돼있기 때문에
    어디서 예상치 못 한 오류가 발생할지 알 수 없다.

둘 이상의 컬렉션은 페치 조인 할 수 없다

  • ONE to MANY 상황에서 페치 조인하고 거기에 또 컬렉션을 페치 조인 하는 상황을 의미한다.
  • ONE to MANY에서도 데이터가 뻥튀기가 되는데 거기에 컬렉션을 한 번 더 페치 조인 시키면
    데이터가 얼마나 뻥튀기 될 지 모르기에 하면 안 된다.

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

  • 일대일, 다대일 같은 단일 값 연관 필드들은 페치 조인을 해도 페이징이 가능하다.
    • 데이터 뻥튀기가 되지 않기 때문
  • 컬렉션을 페치 조인한 경우엔 데이터가 뻥튀기 되기 때문에 페이징을 사용해도 
    뻥튀기 된 데이터에서 페이징을 하는 것이므로 순서와 기준 모든게 맞지 않게 된다.
  • 하이버네이트는 경고 로그를 남기고 메모리에서 페이징(매우 위험)

컬렉션 페치 조인 해결 방법

  1. 페치 조인을 역으로 거는 것이다.
    예를 들어 select t from Team t join fetch t.Member 이었다면 이 일대다 관계를 역으로
    select m from Member m join fetch m.Team 으로 해주는 것이다. 이럼 데이터 뻥튀기가 
    발생하지 않기에 페이징 사용이 가능하다.
  2. 좀 더 근본적인 문제로 돌아가보자 우리가 페치 조인을 사용했던 이유는 N+1 문제를 해결하기 위함이었다.
    그렇기에 N+ 1 문제만 해결하면 페치 조인을 사용할 이유가 없다.
    그에 대한 해결 방법으로 @BatchSize를 사용하는 것이다.
    • 배치 사이즈를 100으로 잡으면 한 번 select 쿼리가 나갈 때 100개의 데이터를 한 번에 불러온다.
      즉 팀에 멤버가 100명이었으면 1+100개의 쿼리가 나가는 것을 1+1 쿼리로 줄일 수 있다는 얘기다.
    • 실무에서는 글로벌 옵션으로 Hibernate.default_batch_fetch_size를 설정하여 기본 적으로 배치가 적용되게 한다

페치 조인 특징

  1. 페치 조인은 연관된 엔티티들을 SQL 한 번으로 조회 - 성능 최적화를 위한 방법이다.
  2. 엔티티에 직접 적용하는 글로벌 로딩 전략보다 우선한다.
    • fetch = FetchType.LAZY
  3. 실무에서 글로벌 로딩 전략은 모두 지연 로딩이다.
  4. 최적화가 필요한 곳은 페치 조인을 적용하자.

정리

  • 모든 것을 페치 조인으로 해결할 수는 없다.
  • 페치 조인은 객체 그래프를 유지할 때 사용하면 효과적이다.
    (member.team 과 같이 원형을 유지하며 탐색할 경우 효과적이란 말 )
  • 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 하면,
    페치 조인 보다는 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는 것이 효과적이다.

 

다형성 쿼리

 

다음과 같이 부모 클래스를 extends 한 자식 클래스들이 있는 경우 몇 가지 특수 기능을 제공한다.

 

TYPE

  • 조회 대상을 특정 자식으로 한정할 수 있다.
    • 예) Item 중에 bool, movie를 조회해라
    • [JPQL]
      select i from item i
      where type(i) in (book, movie)
    • [SQL]
      select i from i
      where i.dtype in('B','M')

TREAT

부모 클래스를 자식 클래스처럼 다운캐스팅해서 다룰 수 있는 함수

  • 예) 부모인 Item과 자식 Book이 있다.
  • [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'

 

참고: 자바 ORM 표준 JPA 프로그래밍 - 기본편