Posts [자바 ORM 표준 JPA 프로그래밍-기본편] 페치조인2 - 한계
Post
Cancel

[자바 ORM 표준 JPA 프로그래밍-기본편] 페치조인2 - 한계

본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.


페치 조인의 특징과 한계

1) 페치 조인 대상에는 별칭(alias)을 줄 수 없다.

  • 하이버네이트는 가능, 가급적 사용X
  • 예시
1
2
3
String query = "select distinct t from Team t join fetch t.members as m where m.age > 10";
List<Team> result = em.createQuery(query, Team.class)
        .getResultList();
  • 왜 안되냐? 기본적으로 나와 연관된 것들을 다 긁어오는 것인데 5명중 3명만 불려와서 조작하면 이상하게 동작할 수 있다. 잘못하면 이상한 데이터가 삭제 될 수 있다.
  • 차라리 별도의 쿼리로 Member 5개만 불러오는게 맞다.
  • 별칭이 기본적으로 허용된다면 데이터의 정합성이나 객체 그래프의 사상에 맞지 않게 된다.
  • 하지만 join fetch a join fetch b … 처럼 할때가 있는데 그때만 딱 쓴다.

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

  • 데이터 정합성에 안맞는다.
  • 잘못하면 데이터가 예상치 못하게 팍팍 늘어나면서 곱하기 곱하기가 될 수 있다.
  • 딱 하나만 지정할 수 있다.

3) 컬렉션(일대다)을 페치 조인하면 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다.

  • image
  • 만약 여기서 페이징이 적용되면 회원1만 남게된다. 그러면 팀A는 회원1만 가지고 있는걸로 되버린다. 문제가 있다.
  • 일대일, 다대일 같은 단일 값 연관 필드들은 페치 조인해도 페이징 가능
  • 하이버네이트는 경고 로그를 남기고 메모리에서 페이징(매우 위험)
  • 실제 쿼리에 페이징 구문이 없다? -> 팀이 100만건이면 100만건을 다 메모리에 올리고 페이징한다. 바로 그냥 망한다.
  • 세 가지 해결법이 있다.
    • 1)회원에서 팀으로 방향을 반대로 뒤집어 해결하는 방법이 있다.
    1
    2
    3
    4
    5
    
    String query = "select m from Member m join fetch m.team t";
    List<Member> result = em.createQuery(query, Member.class)
            .setFirstResult(0)
            .setMaxResults(1)
            .getResultList();
    
    • 2)@BatchSize(size = 100) 을 지정하여 해결할 수 가 있다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    @Entity
    public class Team {
    
      @Id
      @GeneratedValue
      private Long id;
    
      private String name;
    
      @BatchSize(size = 100)
      @OneToMany(mappedBy = "team")
      private List<Member> members = new ArrayList<>();
      ...
    }
    
    public class JpaMain {
      public static void main(String[] args) {
        ...
    
        String query = "select t from Team t";
        List<Team> result = em.createQuery(query, Team.class)
                .setFirstResult(0)
                .setMaxResults(2)
                .getResultList();
    
        System.out.println("result.size() = " + result.size());
    
        for (Team team : result) {
            System.out.println("team = " + team.getName() + " | " + team.getMembers().size());
            for (Member member : team.getMembers()) {
                System.out.println("- members = " + member);
            }
        }
          
        tx.commit();
      }
    }
    
    SQL:
    Hibernate: 
      /* load one-to-many jpql.Team.members */ select
          members0_.TEAM_ID as team_id5_0_1_,
          members0_.id as id1_0_1_,
          members0_.id as id1_0_0_,
          members0_.age as age2_0_0_,
          members0_.TEAM_ID as team_id5_0_0_,
          members0_.type as type3_0_0_,
          members0_.username as username4_0_0_ 
      from
          Member members0_ 
      where
          members0_.TEAM_ID in (
              ?, ?
          )
    
    • 처음에 Team테이블 select 쿼리 1번 + List결과에 담긴 Team객체를 100개씩 in절에 담아 members객체에 채워넣는다.
    • global 세팅에 가져갈 수 있다.
    1
    
    <property name="hibernate.default_batch_fetch_size" value="100" />
    
    • 3)DTO로 쿼리 직접 짜는 방법(DTO로 뽑아도 정제해줘야하기에 만만치 않다)

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

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

  • @OneToMany(fetch = FetchType.LAZY) //글로벌 로딩 전략

7) 실무에서 글로벌 로딩 전략은 모두 지연 로딩

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

페치 조인 - 정리

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

복잡한 조인이 많은 쿼리의 경우(통계성 쿼리 같은 경우) 사실 세 가지 방법이 있다.

1) 페치 조인으로 엔티티를 조회해 와서 그대로 쓴다

2) 페치 조인 열심히해서 애플리케이션에서 DTO로 바꿔쓴다.

3) 아예 처음부터 조인해서 DTO로 결과를 받아와 쓴다.

This post is licensed under CC BY 4.0 by the author.

[자바 ORM 표준 JPA 프로그래밍-기본편] 페치조인1 - 기본

[자바 ORM 표준 JPA 프로그래밍-기본편] 다형성 쿼리