有一个简单的餐厅实体,它有一个投票列表(没有显示不必要的方法和字段)
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;
}
当我使用 Spring Data JPA 中的 findAll() 方法获取数据并像这样通过 DTO 映射器进行转换时
@Cacheable(value = "restaurantDTOList", key = "-1")
public List<RestaurantResponseDTO> getAll() {
List<Restaurant> restaurantList = restaurantRepository.findAll();
return restaurantList.stream()
.map(RestaurantMapper::toRestaurantDto)
.collect(Collectors.toList());
}
公共静态 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 而不是一个,我将不胜感激。
这是预期的行为。
以下是文档所说的
FetchMode.SUBSELECT:免费翻译:
当您的主查询返回多个实体(并且它们很大,即包含大量数据)并且与它们关联的实体很多时,使用此模式是有意义的。在这种情况下,何时
FetchMode.SELECT会发出N+1请求,这是您想要避免的。使用FetchMode.JOIN它会导致主实体中的列在结果中重复。这是对将要传输大量数据的事实的重复(即对于每个关联实体,父级中的数据将重复,记住,我们谈论的是父级很大的情况),这不加入就无法转移,并单独请求获取关联实体。就是这样,适合这种情况
FetchMode.SUBSELECT。