본문 바로가기
JPA

[JPA] 일대다 조인할때 영속성 컨텍스트 내부 모습 & distinct

by onejunu 2020. 8. 6.

 

엔티티와 데이터베이스를 매핑을 아래와 같이 한다.

@Entity
class User{
    ...
    
    @ManyToOne(fetch = FetchType.LAZY )
    private Team team;
    
    ...

}


@Entity
class Team{

    @OneToMany(mappedBy = "team")  // 기본적으로 LAZY
    private List<User> users = new ArrayList<>();
    ...

}

데이터 베이스는 미리 데이터를 인서트 해놓았다.

데이터 베이스 상황

 

팀을 기준으로 유저들을 join 하면 재밌는 일이 발생한다. 즉 아래와 같은 코드가 있을때 출력을 생각해보자.

 

List<Team> teams = em.createQuery("select t from Team t join fetch t.users").getResultList();

for (Team t : teams) {
    System.out.println("####"+t.getName());
    for (User u : t.getUsers()) {
        System.out.println("--"+u.getName());
    }
}

 

결과

이상하지 않은가?  " group1 " 이 2번이나 출력이 되었다.  마지막 "group2" 까지 총 3번이 출력이 되었다.

 

팀은 분명히 2개 밖에 만들지 않았는데 어떻게 3개나 출력이 되는 것인가??

 

먼저 조인한 테이블 부터 보자.

 

조인 결과

 

여기서 JPA는 영속성 컨텍스트에 어떻게 테이블을 저장하는 것인가?? 1줄씩 살펴보자.

 

1. 먼저 team 객체 "group1" 을 영속성 컨텍스트에 집어 넣는다. 또한 user 객체 "wonjun"을 집어넣는다. 왜냐면 fetch join이므로.

 

 

 

2. group1을 반환리스트에 추가한다. 하지만 Team 안에는 이미 group1 이 있기 때문에 참조만 한다. 또한 ujin 객체를 추가한다.

 

3. group2 를 반환리스트에 추가한다. Team에 group2가 없으므로 group2를 추가하고 hoho 객체를 추가한다.

 

4. 반환 리스트에 있는 객체를 반환한다.

 

 

위와 같은 이유 때문에 3번이 출력되는 것이다. 다시 말하면 초록박스 3개가 결과물이다.

 

그러면 중복된 초록박스를 어떻게 제거하는가???

 

그것이 바로 distinct 다. 흔히 아는 sql의 distinct 와는 조금 다르게 동작한다. 

 

원래의 distinct는 모든 필드의 값이 동일한 것들을 모두 제거해 주지만 JPA에서는 같은 객체를 참조하는 반환리스트들을 제거해준다. 

 


 

아래와 같이 DISTINCT 를 사용하여 출력해 보자.

 

List<Team> teams = em.createQuery("select distinct t from Team t join fetch t.users").getResultList();

for (Team t : teams) {
    System.out.println("####"+t.getName());
    for (User u : t.getUsers()) {
        System.out.println("--"+u.getName());
    }
}

결과

 

위와 같이 나오는 이유는 영속성 컨텍스트에서 같은 참조를 하는 초록박스를 제거했기 때문이다. 

 

 

 

 

댓글