페치 조인

Mina Park·2022년 10월 30일
0

1. 기본개념

  • SQL의 조인 종류가 X
  • JPQL에서 성능 최적화를 위해 제공하는 전용 기능
  • 연관된 엔티티/컬렉션을 SQL 한 방에 함께 조회하도록 함
  • 명령어 'join fetch' 직접 사용
    • LEFT/INNER JOIN FETCH + 조인경로
  • 지연로딩이 걸려있더라도 페치조인이 우선시됨

2. 엔티티 페치조인

  • 지연로딩인 경우
//(1)지연로딩
            String query = "select m from Member m";
            List<Member> res = em.createQuery(query, Member.class).getResultList();

            for (Member member : res) {
                System.out.println("member = " + member.getUsername() + member.getTeam().getName());
                //member1: SQL(teamA)
                //member2: 1차캐시(teamA)
                //member3: SQL(teamB)
                //N + 1 문제 발생
            }
  • 사용 코드
//(2)엔티티 페치조인
            String query2 = "select m from Member m join fetch m.team";
            List<Member> res2 = em.createQuery(query2, Member.class).getResultList();

            for (Member member : res2) {
                System.out.println("member with fetch join = " + member.getUsername() + member.getTeam().getName());
                //1번의 쿼리로 실행(프록시가 아닌 실제 엔티티, 모두 영속성 컨텍스트에서 관리)
            }
  • 실행 쿼리
Hibernate: 
    /* select
        m 
    from
        Member m 
    join
        fetch m.team */ select
            member0_.Id as id1_0_0_,
            team1_.Id as id1_3_1_,
            member0_.age as age2_0_0_,
            member0_.TEAM_ID as team_id5_0_0_,
            member0_.type as type3_0_0_,
            member0_.username as username4_0_0_,
            team1_.name as name2_3_1_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.Id
member = 회원1팀A
member = 회원2팀A
member = 회원3팀B

3. 컬렉션 페치조인

  • 일대다 관계인 경우
  • 컬렉션의 경우 '다'에 맞춰서 중복된 데이터가 조회됨(데이터 뻥튀기가 발생)
  • 사용 코드
 //(3)컬렉션 페치조인
            String query3 = "select t from Team t join fetch t.members";
            List<Team> res3 = em.createQuery(query3, Team.class).getResultList();

            for (Team team : res3) {
                System.out.println("member with fetch join = " + team.getName() + "/" + team.getMembers().size());
                //컬렉션(멤버) 수에 맞춰서 중복 로우가 조회됨
                //teamA/2
                //teamA/2
                //teamB/1
            }
  • 실행 쿼리
Hibernate: 
    /* select
        t 
    from
        Team t 
    join
        fetch t.members */ select
            team0_.Id as id1_3_0_,
            members1_.Id as id1_0_1_,
            team0_.name as name2_3_0_,
            members1_.age as age2_0_1_,
            members1_.TEAM_ID as team_id5_0_1_,
            members1_.type as type3_0_1_,
            members1_.username as username4_0_1_,
            members1_.TEAM_ID as team_id5_0_0__,
            members1_.Id as id1_0_0__ 
        from
            Team team0_ 
        inner join
            Member members1_ 
                on team0_.Id=members1_.TEAM_ID
member with fetch join = 팀A/2
member with fetch join = 팀A/2
member with fetch join = 팀B/1

4. 페치조인과 DISTINCT

  • SQL의 DISTINCT: 중복된 결과를 제거
  • JPQL의 DISTINCT: 2가지 기능 제공
    • SQL에 DISTINCT 추가: 사실 데이터가 다르므로 SQL 결과로는 중복제거에 실패
    • 애플리케이션에서 엔티티 중복 제거(식별자의 동일 여부로 판단): 같은 식별자를 가진 엔티티 제거
  • 사용 코드
//(4)페치조인 + distinct => SQL에 distinct 추가(사실 데이터가 다르므로 유효X) + 애플리케이션에서 중복 제거
            String query4 = "select distinct t from Team t join fetch t.members";
            List<Team> res4 = em.createQuery(query4, Team.class).getResultList();

            for (Team team : res4) {
                System.out.println("member with fetch join = " + team.getName() + "/" + team.getMembers().size());
                //같은 식별자를 가진 엔티티 제거
                //teamA/2
                //teamB/1
            }
  • 실행 쿼리
Hibernate: 
    /* select
        distinct t 
    from
        Team t 
    join
        fetch t.members */ select
            distinct team0_.Id as id1_3_0_,
            members1_.Id as id1_0_1_,
            team0_.name as name2_3_0_,
            members1_.age as age2_0_1_,
            members1_.TEAM_ID as team_id5_0_1_,
            members1_.type as type3_0_1_,
            members1_.username as username4_0_1_,
            members1_.TEAM_ID as team_id5_0_0__,
            members1_.Id as id1_0_0__ 
        from
            Team team0_ 
        inner join
            Member members1_ 
                on team0_.Id=members1_.TEAM_ID
member with fetch join = 팀A/2
member with fetch join = 팀B/1

5. 페치 조인 vs 일반 조인

  • 일반조인만 실행해서는 연관된 엔티티를 조회해 오지 않음
  • JPQL은 결과를 반환할 때 연관관계를 고려하지는 않음
    • 단지 SELECT 절에 지정한 엔티티만 조회할 뿐
  • 페치조인을 사용해야만 연관된 엔티티를 함께 조회해옴(즉시로딩처럼)
    • 페치조인은 객체 그래프를 SQL 한방에 조회
  • 일반 조인
  • 페치 조인

0개의 댓글