문제
@Query("select ac from Accommodation ac join fetch ac.roomList r" +
"join fetch r.roomProductList rp where ac.id = :accommodationId" +
"AND rp.startDate = :startDate")
c.a.global.error.GlobalExceptionHandler : org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.aroom.domain.room.model.Room.roomProductList, com.aroom.domain.accommodation.model.Accommodation.roomList]
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.aroom.domain.room.model.Room.roomProductList, com.aroom.domain.accommodation.model.Accommodation.roomList]
문제해석
//Accommodation.java
@JsonManagedReference
@OneToMany(mappedBy = "accommodation", fetch = FetchType.LAZY)
private List<Room> roomList = new ArrayList<>();
//Room.java
@JsonManagedReference
@OneToMany(mappedBy = "room", fetch = FetchType.LAZY)
private List<RoomProduct> roomProductList = new ArrayList<>();
- 2개의 OneToMany 자식테이블에 Fetch Join을 선언했을 때 발생하는
MultipleBagFetchException
💡
MultipleBagFetchException
- 여러 BagType을 동시에 Fetch할때, 발생하는 예외
- 여기서 Bag Type이란 ?
- Set과 같이 순서가 없고, List와 같이 중복을 허용하는 자료구조
- 단, Java Collection에서는 Bag이 없기 때문에 Hibernate에서는 List를 Bag으로 사용 중
⇒ roomList와 roomProductList 모두 List Type이기 때문에 MultipleBagFetchException 발생
해결방안
1. Bag을 Set으로 바꿔서 한번에 쿼리를 진행한다.
//Accommodation.java
@JsonManagedReference
@OneToMany(mappedBy = "accommodation", fetch = FetchType.LAZY)
private Set<Room> roomSet = new HashSet<>();
//Room.java
@JsonManagedReference
@OneToMany(mappedBy = "room", fetch = FetchType.LAZY)
private Set<RoomProduct> roomProductSet = new HashSet<>();
- List가 1개만 있으면 Multiple Bag이 아니므로, Set으로 변경해준다.
2. Join Fetch를 한번만 진행한다.
@Query("SELECT ac FROM Accommodation ac " +
"JOIN FETCH ac.roomList r " +
"WHERE ac.id = :accommodationId AND EXISTS (" +
"SELECT rp FROM RoomProduct rp " +
"WHERE rp.room.id = r.id AND rp.startDate = :startDate and" +
"(rp.startDate between :startDate and :endDate))")
- Accommodation 자체에 2개의 Join Fetch를 수행하지 않고, Room만 Join Fetch를 한다.
- 이후, RoomProduct는 EXISTS문을 통해 조건절로 수행한다.
Uploaded by N2T