본문 바로가기
Spring/JPA

페치조인

by o3oppp 2024. 1. 15.

페치조인(fetch join)

  • JPQL에서 성능 최적화를 위해 제공하는 기능으로 SQL 조인 종류가 아님
  • 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
  • join fetch 명령어 사용

N+1 문제

Team teamA = new Team();
teamA.setName("팀A");
em.persist(teamA);

Team teamB = new Team();
teamB.setName("팀B");
em.persist(teamB);

Member member1 = new Member();
member1.setUsername("회원1");
member1.setTeam(teamA);
em.persist(member1);

Member member2 = new Member();
member2.setUsername("회원2");
member2.setTeam(teamA);
em.persist(member2);

Member member3 = new Member();
member3.setUsername("회원3");
member3.setTeam(teamB);
em.persist(member3);

em.flush();
em.clear();

String query = "select m From Member m";
List<Member> result = em.createQuery(query,Member.class).getResultList();

for (Member member : result) {
    System.out.println("member = " + member.getUsername() + " , " + member.getTeam().getName());
}
... 

  • 회원1(SQL)
  • 회원2(1차캐시)
  • 회원3(SQL)
  • N+1 문제 발생 -> 페치 조인으로 해결

N+1 문제 해결

...
String query = "select m From Member m join fetch m.team";
List<Member> result = em.createQuery(query,Member.class).getResultList();

for (Member member : result) {
    System.out.println("member = " + member.getUsername() + " , " + member.getTeam().getName());
}
...

  • result는 프록시가 아닌 실제 데이터
  • 지연로딩으로 설정해도 페치조인이 우선이므로 즉시 로딩

DISTINCT

  • JPQL의 DISTINCT는 2가지 기능을 제공
    1. SQL에 DISTINCT 기능
    2. 애플리케이션에서 엔티티 중복 제거
  • 사용 전
String query = "select t from Team t join fetch t.members";
List<Team> result = em.createQuery(query, Team.class).getResultList();

for (Team team : result) {
  System.out.println("team = " + team.getName() + " , " + team.getMembers().size());
  for(Member member : team.getMembers()){
      System.out.println("member = " + member);
  }
}

  • 사용 후
String query = "select distinct t from Team t join fetch t.members";
List<Team> result = em.createQuery(query, Team.class).getResultList();

for (Team team : result) {
  System.out.println("team = " + team.getName() + " , " + team.getMembers().size());
  for(Member member : team.getMembers()){
      System.out.println("member = " + member);
  }
}

  • 결론적으로 위에서 말한 애플리케이션 중복 제거
  • 같은 식별자를 가진 Team 엔티티가 제거 됨

일반 조인과 차이

  • 일반 조인 실행 시 연관된 엔티티를 함께 조회하지 않음, 단지 SELECT 절에 지정한 엔티티만 조회
  • 페치 조인 실행 시 연관된 엔티티도 함께 조회(즉시 로딩)
  • 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념

한계

  • 페치 조인 대상에는 별칭 불가
String query = "select t from Team t join fetch t.members m" // t.members의 별칭 m이 문제
  • 둘 이상의 컬렉션은 패치 조인 불가
  • **컬렉션을 페치 조인하면 페이징API(setFirstResult, setMaxResult) 사용 불가
  • 엔티티에 직접 적용하는 글로벌 전략(@OneToMany(fetch=FetchType.LAZY) 등)보다 우선함
  • 즉 글로벌 전략으로 지연 로딩 설정하여도 즉시 로딩

특징

  1. 성능 최적화가 가능 -> 연관된 SQL 한번에 조회하므로
  2. 객체 그래프를 유지할 때 사용하면 효과적
  3. 글로벌 로딩 전략은 모두 지연 로딩

'Spring > JPA' 카테고리의 다른 글

벌크연산(Bulk Operation)  (0) 2024.01.17
Named 쿼리  (0) 2024.01.17
값 타입 컬렉션  (0) 2024.01.02
임베디드 타입  (0) 2023.12.30
영속성 전이  (0) 2023.12.29