[JPA] Fetch Join

밀크야살빼자·2023년 5월 29일
0
post-thumbnail

Join과 Fetch Join 차이점

  • Join
    • Fetch Join과 달리 연관 Entity에 Join을 걸어도 실제 쿼리에서 SELECT하는 Entity는 오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화
    • 조회의 주체가 되는 Entity만 SELECT해서 영속화하기 때문에 데이터는 필요하지 않지만 연관 Entity가 검색조건에는 필요한 경우에 주로 사용됨
  • Fetch Join
    • 조회의 주체가 되는 Entity 이외에 Fetch Join이 걸린 연관 Entity도 함께 SELECT하여 모두 영속화
    • Fetch Join이 걸린 Entity 모두 영속화하기 때문에 FetchType이 Lazy인 Entity를 참조하더라도 이미 영속성 컨텍스트에 들어있기 때문에 따로 쿼리가 실행되지 않은 채로 N+1 문제가 해결됨

Team.java

@Entity
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Setter
@ToString
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "team")
    @Builder.Default
    private List<Member> members = new ArrayList<>();
    
    public void addMember(Member member) {
        member.setTeam(this);
        members.add(member);
    }
}

Member.java

@Entity
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Setter
@ToString
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    private int age;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
}

Join

@Query("SELECT distinct t FROM Team t JOIN t.members")
public List<Team> findAllWithMembersUsingJoin();
Hibernate:
    select
         distinct team0_.id as id1_1_,
         team0_.name as name2_1_
    from team team0_ 
    inner join
        member members1_
            on team0_.id=members1_.team_id
  • Join 조건을 제외하고 실제 질의하는 대상 Entity에 대한 컬럼만 select
  • Team과 Member가 Join된 형태의 쿼리가 실행되지만 가져오는 컬럼들은 Team의 컬럼인 id와 name만 가져온다.
  • Join을 했는데 각 Team의 Lazy Entity인 members가 아직 초기화되지 않음
  • 실제로 일반 Join은 실제 쿼리에 Join을 걸어주기는 하지만 Join 대상에 대한 영속성가지는 관여하지 않음
  1. 일반 Join으로 Team Entity 초기화 완료
  2. 하지만 일반 Join은 연관 Entity까지 초기화하지 않기 때문에 Member는 초기화되지 않음
  3. toString()으로 아직 초기화되지 않은 members에 접근하면서 LazyInitializationException 발생(실제로 Team에 @ToString(exclude="members")를 설정하게 되면 members에 접근하지 않게 되고 LazyInitializationException 또한 발생하지 않게 됨)

Fetch Join을 활용한 N+1

@Query("SELECT distinct t FROM Team t JOIN FETCH t.members")
public List<Team> findAllWithMembersUsingFetchJoin();
Hibernate:
    select
         distinct team0_.id as id1_1_,
         members1_.id as id1_0_1_,
         team0_.name as name2_1_,
         members1_.age as age2_0_1,
         members1_.name as name3_0_1_,
         members1_.team_id as team_id4_0_1_,
         members1_.team_id as team_id4_0_1_,
         members1_.id as id1_0_1_
    from team team0_ 
    inner join
        member members1_
            on team0_.id=members1_.team_id
  • 실제 질의하는 대상 Entity와 Fetch Join이 걸려있는 Entity를 포함한 컬럼 함께 select한다

📜 참고

https://frogand.tistory.com/156
https://cobbybb.tistory.com/18

profile
기록기록기록기록기록

0개의 댓글