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

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

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


JPQL - 페치 조인(fetch join)

실무에서 정말정말 중요함

  • SQL 조인 종류X
  • JPQL에서 성능 최적화를 위해 제공하는 기능
  • 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
  • join fetch 명령어 사용
  • 페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로

엔티티 페치 조인

  • 회원을 조회하면서 연관된 팀도 함께 조회(SQL 한 번에)
  • SQL을 보면 회원 뿐만 아니라 팀(T.*)도 함께 SELECT
  • JPQL
1
select m from Member m join fetch m.team
  • SQL
1
2
SELECT M.*, T.* FROM MEMBER M
INNER JOIN TEAM T ON M.TEAM_ID=T.ID

image

페치 조인 사용 코드(예시)

  • 페치 조인을 안 쓴 예시
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
Team teamA = new Team();
teamA.setName("teamA");
em.persist(teamA);

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

Member member1 = new Member();
member1.setUsername("회원1");
member1.setAge(10);
member1.setType(MemberType.ADMIN);
member1.changeTeam(teamA);
em.persist(member1);

Member member2 = new Member();
member2.setUsername("회원2");
member2.setAge(10);
member2.setType(MemberType.ADMIN);
member2.changeTeam(teamA);
em.persist(member2);

Member member3 = new Member();
member3.setUsername("회원3");
member3.setAge(10);
member3.setType(MemberType.ADMIN);
member3.changeTeam(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, 팀A (SQL 호출)
    //회원2, 팀A (1차 캐시)
    //회원3, 팀B (SQL 호출)

    //회원 100명 -> N(첫 쿼리로 받아온 데이터의 갯수) + 1(Member를 가져온 첫 쿼리)
}
  • 페치 조인 적용 예시
1
2
3
4
5
6
7
8
9
10
11
12
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, 팀A (프록시가 아닌 실제 객체)
    //회원2, 팀A (프록시가 아닌 실제 객체)
    //회원3, 팀B (프록시가 아닌 실제 객체)

    //회원 100명 -> N(첫 쿼리로 받아온 데이터의 갯수) + 1(Member를 가져온 첫 쿼리)
}

컬렉션 페치 조인

  • 일대다 관계, 컬렉션 페치 조인
  • [JPQL]
1
2
3
select t
from Team t join fetch t.members
where t.name = ‘팀A'
  • [SQL]
1
2
3
4
5
SELECT T.*, M.*
FROM TEAM T
  INNER JOIN MEMBER M 
  ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'

컬렉션 페치 조인 사용 코드

1
2
3
4
5
6
7
8
9
10
11
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("- members = " + member);
    }
}
  • 결과

image

일대다 조인은 데이터 뻥튀기 될 수 있다.

image

팀A에 연관된 Member가 2개 있다.

image

그럼 위의 이미지처럼 팀 입장에선 하나지만 멤버는 두명이기에 두 개를 만들어낸다.

image

같은 ID값을 가지기에 영속성 컨텍스트에는 1개의 TeamA객체만 올라간다. 결론적으로 같은 주소값을 가진 두 개가 출력된다.

페치 조인과 DISTINCT

  • SQL의 DISTINCT는 중복된 결과를 제거하는 명령
  • JPQL의 DISTINCT 2가지 기능 제공
    • 1)SQL에 DISTINCT를 추가
    • 2)애플리케이션에서 엔티티 중복 제거
1
2
3
select distinct t
from Team t join fetch t.members
where t.name = ‘팀A’
  • SQL에 DISTINCT를 추가하지만 데이터가 다르므로 SQL 결과 에서 중복제거 실패(레코드의 모든 레코드값이 일치하지 않으므로)

image

  • DISTINCT가 추가로 애플리케이션에서 중복 제거시도
  • 같은 식별자를 가진 Team 엔티티 제거

image

  • [DISTINCT 추가시 결과]
1
2
3
teamname = 팀A, team = Team@0x100
-> username = 회원1, member = Member@0x200
-> username = 회원2, member = Member@0x300

image

Note: 다대일 관계는 아무 문제 없다. 일대다만 데이터가 뻥튀기 된다.

페치 조인과 일반 조인의 차이

  • 일반 조인 실행시 연관된 엔티티를 함께 조회하지 않음
  • 조인 쿼리만 실행되는거지 실제 select 절에 연관 엔티티 관련된게 없음
  • [JPQL]
1
2
3
select t
from Team t join t.members m
where t.name = ‘팀A'
  • [SQL]
1
2
3
4
SELECT T.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'
  • JPQL은 결과를 반환할 때 연관관계 고려X
    • 딱 SELECT 절에 있는 것만 가져올 뿐
  • 단지 SELECT 절에 지정한 엔티티만 조회할 뿐
  • 여기서는 팀 엔티티만 조회하고, 회원 엔티티는 조회X
  • 페치 조인을 사용할 때만 연관된 엔티티도 함께 조회(즉시 로딩)
  • 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념
This post is licensed under CC BY 4.0 by the author.

[자바 ORM 표준 JPA 프로그래밍-기본편] 경로표현식

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