Skip to content

MultipleBagFetchException

khw7385 edited this page Feb 24, 2025 · 2 revisions

Bag 란?

Hibernate 에서 사용되는 컬렉션으로 중복을 허용을 하지만 순서가 없는 컬렉션이다.

하이버네이트 동작 과정

쿼리의 결과는 하이버네이트의 ResultSet 으로 가져온다. 이 때, 쿼리의 대상이 되는 엔티티들은 고유하게 관리되고 만약 해당 엔티티 내부에 컬렉션 데이터들은 해당되는 하이버네이트의 자료구조로 관리가 된다.

image

예를 들어, Team 엔티티의 member 컬렉션이 있을 때 이 컬렉션은 하이버네이트에서 PersistentBag 으로 관리가 된다.

MultipleBagFetchException

@Query("SELECT p FROM Plan p " +
            "JOIN FETCH p.feedback " +
            "JOIN FETCH p.paths pa " +
            "JOIN FETCH pa.todos t " +
            "JOIN FETCH t.category c " +
            "WHERE p.member = :member AND t.type = :type AND p.createdAt between :startDate AND :endDate"
    )
    List<Plan> findByMemberIdAndCreatedAtBetweenOrderByCreatedAtDesc(Member member, TodoType type, LocalDateTime startDate, LocalDateTime endDate);

MultipleBagFetchException 예외에 대해서 발생하는 이유에 대해서 알아보자. Plan 엔티티를 조회할 bag 컬렉션이 두개가 존재할 것이다. 두 개이상의 bag 에서 fetch 하려고 해서 해당 예외가 발생한다.

그럼, 왜 예외가 터뜨리는 것일까? 바로 데이터의 중복으로 인한 문제이다!! 하나의 엔티티에 여러 컬렉션이 있든, 체이닝 1:N 관계이든 데이터의 중복이 발생하여서 컬렉션을 List 로 하면 하이버네이트에서 에러를 발생시킨다.

⇒ 두 개 이상의 컬렉션을 Fetch Join 하면 JPA는 어느 컬렉션에 어떤 데이터를 넣어야 하는지 결정할 수 없음.

해결책

  1. 엔티티의 컬렉션 자료구조를 List → Set 으로 변경
    • Set 은 중복을 허용하지 않아서 해결이 가능하지만 중복을 허용하지 않기 때문에 성능 문제가 발생할 수 있다.
    • fetch join 으로 가져온 데이터는 한 번에 로드되지만 하이버네이트는 PersistentSet 에 추가하면서 중복 여부를 검사한다.
  2. @BatchSize 를 이용하여 해결
    • 글로벌 설정이든, 엔티티에 붙이든, 엔티티의 컬렉션에 붙이든 batch size 설정을 통해 한 번에 여러 데이터를 가져온다.
    • 하나의 엔티티에 여러 컬렉션이 있을 때는 페치 조인을 일반적으로 데이터가 많은 쪽에 걸어주고 반대편에 배치 사이즈를 이용한다.
    • 체이닝 페치 조인의 경우는 앞 쪽에 페치 조인을 적용할 수 밖에 없다.

Clone this wiki locally