[JPA 기본 - JPQL] 페치 조인 ⭐️

강신현·2022년 7월 27일
0

✅ 엔티티 페치 조인 ✅ 컬렉션 페치 조인 ✅ DISTINCT

페치 조인

연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
join fetch

  • SQL 조인 종류가 아니라 JPQL에서 성능 최적화를 위해 제공하는 기능이다.

1. 엔티티 페치 조인

예 : 회원을 조회하면서 연관된 팀도 함께 조회

  • JPQL
select m from Member m join fetch m.team
  • SQL
SELECT M.*, T.* FROM MEMBER M
INNER JOIN TEAM T ON M.TEAM_ID=T.ID

- 예제

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

Team teamB = new Team();
teamB.setName("팀A");
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());
}

Member와 Team의 연관관계가 @ManyToOne(fetch = FetchType.LAZY)으로 지연로딩이기 때문에
회원1 : 팀A -> sql로 조회
회원2 : 팀A -> 1차캐시에서 조회
회원3 : 팀B -> sql로 조회
총 3번의 쿼리가 날라간다.

👉 최악의 경우 팀이 모두 다르다면 회원이 N명일 때 1 + N번의 쿼리가 필요하다.
👉 굉장히 비효율 적이므로 페치 조인을 통해 지연로딩 없이, 한방 쿼리로 조회한다.

String query = "select m From Member m join fetch m.team";

페치 조인 쿼리를 날릴 때 모든 것을 조인하여 영속성 컨텍스트에 올려놓으므로
반복문에서 새로운 쿼리를 날릴 필요가 없다고 보면 됨
👉 페치 조인으로 회원과 팀을 함께 조회해서 지연 로딩이 발생하지 않음


2. 컬렉션 페치 조인

  • 일대다 관계

  • JPQL

select t
from Team t join fetch t.members
where t.name = ‘팀A'
  • SQL
SELECT T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'

- 예제

  • Main
// ... 위와 동일

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() + " | members = " + team.getMembers().size());
}

team = 팀A | members = 2
team = 팀A | members = 2
team = 팀A | members = 1

👉 db상 일대다 조인해서 가져오는 것이기 때문에 같은 정보가 또 조회되는 문제가 있음

- JPQL DISTINCT

  1. SQL에 DISTINCT를 추가
  2. 애플리케이션에서 엔티티 중복 제거
select distinct t
from Team t join fetch t.members
where t.name = ‘팀A’

한계

  • 페치 조인 대상에는 별칭을 줄 수 없다.
  • 둘 이상의 컬렉션은 페치 조인 할 수 없다.
  • 컬렉션을 페치 조인하면 페이징 API를 사용할 수 없다.
    • 일대일,다대일 같은 단일 값 연관 필드들은 페치 조인해도 페이징 가능

결론

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

0개의 댓글