- 문제상황
List<Todo> results = jpaQueryFactory
.selectFrom(todo)
.leftJoin(todo.managers).fetchJoin()
.leftJoin(todo.comments).fetchJoin()
.where(condition)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
- todo조회 시 manager와 comment의 개수를 출력하면서 N+1문제를 해결하기 위해 Fetch Join을 연속으로 두개 사용
- MultipleBagFetchException 발생
-> 2개 이상의 OneToMany 자식 테이블에 Fetch Join을 선언했을때 발생
MultipleBagFetchException의 원인
Hibernate는 여러 개의 List 타입 @OneToMany 또는 @ManyToMany 관계를 Fetch Join으로 동시에 로딩할 때, 내부적으로 "중복된 레코드"를 제거하는 방식 때문에 발생, List는 순서를 보장해야 하기 때문에, 데이터베이스에서 조회된 결과를 그대로 유지해야 하며, 중복 제거 시 복잡성이 증가 이로 인해, Hibernate는 여러 컬렉션을 List로 Fetch Join하는 경우 예외를 던지게됨.
- 해결방법
1. 객체수가 많은 comment는 fetchjoin으로 대비 작은 manager은 BatchSize로 해결
List<Todo> results = jpaQueryFactory
.selectFrom(todo)
.leftJoin(todo.managers)
.leftJoin(todo.comments).fetchJoin()
.where(condition)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
// Todo Entity
public class Todo extends Timestamped {
@OneToMany(mappedBy = "todo", cascade = CascadeType.PERSIST)
@BatchSize(size = 10)
private List<Manager> managers = new ArrayList<>();
}
2. OneToMany부분을 Set으로 받아 처리
@OneToMany(mappedBy = "todo", cascade = CascadeType.REMOVE)
private Set<Comment> comments = new HashSet<>();
@OneToMany(mappedBy = "todo", cascade = CascadeType.PERSIST)
private Set<Manager> managers = new HashSet<>();
'Java & Spring > 트러블슈팅' 카테고리의 다른 글
@Transactional(propagation = Propagation.REQUIRES_NEW) (0) | 2024.10.10 |
---|---|
Self-invocation(자기 호출) (0) | 2024.09.05 |