컬렉션 페치 조인

희운·2025년 4월 30일

JPA

목록 보기
3/5

컬레션 페치 조인이란
"1 대 n " 에서 1 에 해당하는 엔티티와 n 에 해당하는 엔티티와 fetch join 을 하는것이다.

하지만 여기서 문제점은 '다' 에 해당하는 컬렉션과 페치조인을 하면 데이터가 뻥튀기 된다는 문제점이다. 실제 테이블에서 일어나는 일이다.
TeamA -> member1, member2, member3
TeamB -> member4, member5, member 6
가 있다고 가졍했을대 조인을 했을경우 데이터가 많은 member 쪽에 맞춰서 TeamA 가 3개
TeamB가 세개가 될것이다.
(직접 그려서 확인해봐)

하지마 JPA 에서 하이버네이스 6기준 이후로는 이 뻥튀기 된 데이터를 "어플리케이션"단에서
List 에 중복이 있을경우 중복을 알아서 제거해준다.


@Test
    public void findMemberLazy3() {
        //given
        //member1 -> teamA
        //member2 -> teamB

        Team teamA = new Team("teamA");
        Team teamB = new Team("teamB");

        teamRepository.save(teamA);
        teamRepository.save(teamB);

        Member member1 = new Member("member1", 10, teamA);
        Member member2 = new Member("member2", 10, teamA);
        Member member3 = new Member("member3", 10, teamA);
        Member member4 = new Member("member4", 10, teamA);
        Member member5 = new Member("member5", 10, teamB);
        Member member6 = new Member("member6", 10, teamB);
        Member member7 = new Member("member7", 10, teamB);

        memberRepository.save(member1);
        memberRepository.save(member2);
        memberRepository.save(member3);
        memberRepository.save(member4);
        memberRepository.save(member5);
        memberRepository.save(member6);
        memberRepository.save(member7);

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

        List<Team> result = em.createQuery("select t from Team t join fetch t.members m", Team.class)
                .getResultList();


        System.out.println(result.size());

        result.forEach(
                team -> {
                    System.out.println("====== teamName " + team + "=====");
                    team.getMembers().forEach(System.out::println);
                }
        );

}

원래는 데이터가 뻥튀기 되니깐 List<Member> 에는 뻥튀긴 데이터 즉 같은 Team 이 많이 나올 것으로 예상이 되었지만 TeamA , TeamB 이렇게 결과가 나왔다.

이 처럼 어플리케이션 단에서 (java) 에서 일어나는 일이다.

하지만 실제 join 된 결과 sql 에서는 데이터 뻥튀기가 된게 맞음!!!

여기서 중요한게 실제 fetch join 을 컬렉션과 했을경우 페이징을 할수 없다는것이다!!

왜냐하면

페이징 쿼리는 어플리케이션단에서 일어나는게 아니라 실제 SQL 즉 (데이터가 뻥튀긴 된 상태) 를 페이징 하기 때문에 문제가 발생하는것이다.SQL 에 LIMIT, OFFSET 쿼리가 발생한다.

하지만 페이징 쿼리는 DB(SQL)에서 실행되므로,

중복 row 기준으로 LIMIT/OFFSET이 적용됨 → 원하는 만큼의 "Team"이 안 나올 수 있음


그럼 컬레션 페치 조인은 어떻게 하는것이 좋을까??

  • xxToOne 과는 fetch join 을 하고 xxToMany 와는 lazy + batch size 조절을 이용하자.

그리고 나머지 컬렉션은 LAZY 로 하고 BATCH SIZE 를 조정해서 1 + n 문제를 해결하자.

profile
기록하는 공간

0개의 댓글