본문 바로가기
  • 개발공부 및 일상적인 내용을 작성하는 블로그 입니다.
JPA

JPQL - JPQL 페치 조인(2)

by 방구석 대학생 2020. 11. 28.

"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."

www.inflearn.com/course/ORM-JPA-Basic#

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그

www.inflearn.com

 

 

컬렉션 페치 조인

두 객체 간의 관계가 일대다 연관관계일 경우 컬렉션 페치 조인을 할 수 있다.

- 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'

 

아래의 코드를 한 번 실행시켜 보자.

- JpaMain.java

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());
}

- Hibernate SQL

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
team = 팀A| members = 2
team = 팀A| members = 2 /* 팀A 가 왜 두번씩이나 출력되는 걸까?*/
team = 팀B| members = 1

 

위의 Hibernate SQL 을 보면 팀A 에 대한 결과가 두번이나 출력되는 것을 확인할 수 있다.

도대체 왜 이렇게 된 것일까?

 

아래의 그림을 한번 살펴보자.

 

데이터 베이스에서 조인을 하게 되면 각 필드 별로 속성이 같은 데이터들이 한 항목으로 통합되어서 나타나는게 아니라, 각 데이터마다 따로따로 테이블에 나타나기 때문에 위의 코드에서 페치 조인 결과 회원 두명이 있는 팀A 의 결과가 두 번 나오게 된 것이다.

즉, 조회한 컬렉션에는 위의 그림과 같이 같은 주소값을 가지고 있는 결과가 두 줄이 나오게 된다.

 

다음과 같은 코드를 작성하면 똑같은 주소값이 두 번 출력되었다는 것을 명확히 알 수 있다.

- JpaMain.java

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

- Hibernate SQL

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
team = 팀A| members = 2
-> member = Member{id=3, username='회원1', age=0}
-> member = Member{id=4, username='회원2', age=0}
team = 팀A| members = 2
-> member = Member{id=3, username='회원1', age=0}
-> member = Member{id=4, username='회원2', age=0}
team = 팀B| members = 1
-> member = Member{id=5, username='회원3', age=0}

 

그렇다면 이런 중복은 어떻게 제거해 줄 수 있을까?

- 페치 조인과 DISTINCT

SQL 의 DISTINCT 는 중복된 결과를 제거하는 명령이다.

그런데 SQL 의 DISTINCT 는 데이터의 모든 필드 값들이 완전히 똑같아야 중복 제거를 해주는데 반해, 페치 조인으로 인해 생성된 테이블의 경우 같은 팀에 소속되어 있는 회원 끼리라고 해도 각 회원의 ID 값, 즉 내부 조인된 Member 객체의 기본키 값이 서로 다르기 때문에 데이터가 완전히 똑같지 않게 되어 중복을 제거할 수 없게 되는 것이다.

 

위와 같은 이유로 SQL 쿼리만으로는 데이터가 줄여지지 않기 때문에 JPA 에서는 DISTINCT 가 추가로 애플리케이션 에서 중복 제거를 시도한다.

그를 통해 같은 식별자를 가진 Team Entity 가 제거되는 것이다.

- JPQL 의 DISTINCT 는 2가지 기능을 제공한다.

1. SQL 에 DISTINCT 를 추가한다.

2. 애플리케이션에서 Entity 중복을 제거한다.

 

- JpaMain.java

String query = "select distinct t from Team t join fetch t.members";

List<Team> result = em.createQuery(query, Team.class)
	.getResultList();

System.out.println("result = " + result.size());

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

- Hibernate SQL

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
result = 2
team = 팀A| members = 2
-> member = Member{id=3, username='회원1', age=0}
-> member = Member{id=4, username='회원2', age=0}
team = 팀B| members = 1
-> member = Member{id=5, username='회원3', age=0}

위의 Hibernate SQL 을 보면 DISTINCT 명령어를 통해 중복되는 데이터 출력이 제거되었음을 알 수 있다.

 

다음번 글에서는 페치 조인과 일반 조인의 차이점에 대해 알아보자.

'JPA' 카테고리의 다른 글

JPQL - JPQL 페치 조인(4)  (0) 2020.11.28
JPQL - JPQL 페치 조인(3)  (0) 2020.11.28
JPQL - JPQL 페치 조인(1)  (0) 2020.11.28
JPQL - 경로 표현식  (0) 2020.11.28
JPQL - JPQL 기본(10)  (0) 2020.11.24