JPA

JPA 두번 이상의 left join fetch가 필요할 때 해결 방법(MultipleBagFetchException 문제 해결)

khds 2021. 8. 23. 16:23

프로젝트를 진행하면서 두개 이상의 OneToMany 리스트를 가진 객체에 대한 정보를 가져올 때 N+1 문제에 걸리지 않으면서 각각의 리스트들을 가져올 필요가 있었다. 

만약 하나의 OneToMany 리스트만을 같이 다룬다면 left join fetch 를 적용하여 해결할 수 있다. 하지만 문제는 두 개의 리스트라는 점이다. left join fetch는 두 번 이상 사용할 수 없다.(OneToOne는 가능하다) 이는 잘못하면 카티젼 곱과 같은 형식으로 데이터가 전달될 위험이 있기 때문에 서버는 미리 MultipleBagFetchException 를 띄우며 에러처리를 한다. 

그렇다면 이러한 상황에서 어떻게 해야할까? 

 

이는 한번의 select – from where a=b절을 select – from where a in (..) 로 바꾸는 전략으로 해결하면 된다. 

즉, 지정된 수만큼 in절에 부모 Key를 사용하게 되는 것이다.

즉, 1개의 엔티티를 가져오고 그와 연관된 엔티티를 1000번 호출하는 N+1문제를 1개의 엔티티를 가져오고 1000개의 엔티티를 가져오게 하면1+1의 호출로 만드는 것이다. 

이는 join fetch 를 적용했을 때 발생하는 호출 횟수인 1회 보다는 더 많은 호출이 일어난다. 하지만 2개 이상의 OneToMany로 설정된 리스트를 가져와야할 때는 유용한 수단임을 알 수있다.

 

위의 전략을 적용하는 것은 하이버네이트의 default_batch_fetch_size 옵션을 사용하는 것이다.

이를 적용하는 것은 아래와 같다.

 

application.yml

spring:
  jpa:
    properties:
      hibernate: 
      	default_batch_fetch_size: 1000

 

application.properties

spring.jpa.properties.hibernate.default_batch_fetch_size=1000

 

이렇게 설정하면 해당 프로젝트 전체 설정으로 옵션이 적용된다. 

가장 많은 데이터를 가진 자식 엔티티를 join fetch 로 적용하고 나머지를 hibernate.default_batch_fetch_size 로 해결하면 된다.

 

참고로 말하자면 ManyToOne는 여러개의 join fetch 가 되니 위와같은 문제를 걱정할 필요는 없다.

 

참고

https://jojoldu.tistory.com/457