Почему @Fetch(FetchMode.SUBSELECT) тригерит 2 SQL вместо 1?
Есть простое энтити Restaurant, которое имеет список Vote ( Ненужные методы и филды не показаны )
public class Restaurant extends AbstractNamedEntity {
@OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
@Fetch(FetchMode.SUBSELECT)
private List<Vote> votes;
} Вот другая сторона отношений
public class Vote extends AbstractBaseEntity {
@ManyToOne
@JsonBackReference
private Restaurant restaurant;
}
Когда я получаю данные при помощи метода findAll() от Spring Data JPA и конверчу их через ДТО маппер вот так
@Cacheable(value = "restaurantDTOList", key = "-1")
public List<RestaurantResponseDTO> getAll() {
List<Restaurant> restaurantList = restaurantRepository.findAll();
return restaurantList.stream()
.map(RestaurantMapper::toRestaurantDto)
.collect(Collectors.toList());
}
public static RestaurantResponseDTO toRestaurantDto(Restaurant restaurant) {
return new RestaurantResponseDTO(restaurant.getId(), restaurant.getName(),
restaurant.getAddress(), getRestaurantVoteCount(restaurant));
}
public static long getRestaurantVoteCount(Restaurant restaurant) {
var votes = restaurant.getVotes();
if (votes == null) return 0;
return votes.stream().filter(vote -> vote.getVoteDate().equals(LocalDate.now())).count();
} Я получаю эти SQL
Hibernate:
select
restaurant0_.id as id1_1_,
restaurant0_.name as name2_1_,
restaurant0_.address as address3_1_
from
restaurant restaurant0_
Hibernate:
select
votes0_.restaurant_id as restaura3_4_1_,
votes0_.id as id1_4_1_,
votes0_.id as id1_4_0_,
votes0_.restaurant_id as restaura3_4_0_,
votes0_.user_id as user_id4_4_0_,
votes0_.vote_date as vote_dat2_4_0_
from
vote votes0_
where
votes0_.restaurant_id in (
select
restaurant0_.id
from
restaurant restaurant0_
)
Буду крайне благодарен, если поможете разобраться, почему 2 SQL, а не одно.
Ответы (1 шт):
Это ожидаемое поведение.
Вот что говорит документация про FetchMode.SUBSELECT:
When accessing a non-initialized collection, this fetch mode will trigger loading all elements of all collections of the same role for all owners associated with the persistence context using a single secondary select.
Вольный перевод:
При доступе к неинициализированной коллекции этот режим инициирует загрузку всех элементов этой ассоциации для всех сущностей в контексте используя один дополнительный запрос.
Этот режим есть смысл использовать, когда у вас основной запрос возвращает несколько сущностей (и они большие, т.е. содержат много данных), а ассоциированных с ними сущностей много. В этом случае при FetchMode.SELECT сделает N+1 запрос, чего хочется избежать. Использование FetchMode.JOIN приведет к тому, что колонки из основной сущности будут дублироваться в результате. Это дублирование привет к тому, что будет передано много данных (т.е. для каждой ассоциированной сущности данные в родителе будут повторятся, и напоминаю, мы говорим о ситуации, когда родитель - большой), которые можно не передавать, если не делать join, а доставать ассоциированные сущности отдельным запросом.
Вот именно для этой ситуации и годится FetchMode.SUBSELECT.