*/Spring

[JPA] Join Fetch 시 MultipleBagFetchException

sssbin 2024. 11. 7. 15:41

 

문제

프로젝트를 진행하다가

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

이런 에러가 떴다.

원인

하나의 Member 엔티티에서

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<ProfileImage> profileImages;

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Fever> fevers;

두 개의 OneToMany 연관관계를 가지고 있었는데,

@Query("SELECT m FROM Member m " +
        "LEFT JOIN FETCH m.profileImages pi " +
        "LEFT JOIN FETCH m.fevers f " +
        "WHERE m.uuidSocialMember = :id " +
        "AND m.isDeleted = FALSE")
Optional<Member> findWithImagesAndFeversByIdAndIsDeletedIsFalse(UUID id);

이렇게 두 개를 JOIN FETCH 하니까 발생한 것이었다.

이 예외는 여러 Bag를 동시에 fetch하려고 할 때 발생한다고 한다.

Bag(Multiset)은 Set과 같이 순서가 없고, List와 같이 중복을 허용하는 자료구조인데,

Java의 Collection에는 Bag 자료구조가 없기 때문에 Hibernate에서는 List를 Bag로 사용하고 있기 때문에 발생한 것..!

해결 방법

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<ProfileImage> profileImages;

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Fever> fevers;

Set 자료구조로 바꿔주기

자세히는 좀 더 알아봐야 할 것 같음.

 

 


 

저렇게 해결했었는데, 내가 나중에 내린 결론은 일대다 관계의 엔티티는 웬만하면 join fetch를 하지 말자..였다.

왜냐하면 cartesian product 문제로 인해 엔티티 결과가 중복으로 조회될 수 있다.

뭐가 정답인지는 아직 잘 모르겠다.