为什么使用@Fetch(FetchMode.SUBSELECT)时执行2条SQL而非1条?
解答
@Fetch(FetchMode.SUBSELECT) 本身的设计就是产生2条SQL,你遇到的是正常现象,并非代码或配置错误。
核心原因说明
SUBSELECT的执行逻辑分为两步:- 执行主查询:调用
findAll()方法时,Hibernate仅执行第一条SQL查询所有Restaurant实体数据,此时关联的votes是懒加载代理对象,未加载真实数据。 - 触发关联查询:在
getRestaurantVoteCount()方法中调用restaurant.getVotes()时触发懒加载,Hibernate会执行第二条SQL,将第一次查询所有Restaurant的id作为in条件,一次性查出所有餐厅对应的所有Vote数据,直接避免了默认懒加载的N+1问题(即每访问一个餐厅的votes就生成一条SQL的情况)。
- 执行主查询:调用
- 你误以为的「1次SQL查询」是
FetchMode.JOIN的效果,但FetchMode.JOIN仅对findById()这类单实体查询生效,对findAll()批量查询默认失效。如果要实现1次SQL拉取所有数据,可自定义JPQL使用LEFT JOIN FETCH:
注意该写法在单餐厅关联Vote数量较多时,会产生笛卡尔积重复数据,因此需要加@Query("SELECT DISTINCT r FROM Restaurant r LEFT JOIN FETCH r.votes") List<Restaurant> findAllWithVotes();DISTINCT去重。
优化建议
你当前的逻辑仅需要统计当日投票数,把全量历史Vote加载到内存再过滤非常浪费性能,可直接通过聚合查询在数据库层完成统计,或者使用@Formula注解直接映射当日投票数字段,无需加载全量Vote数据。
内容的提问来源于stack exchange,提问作者user15599360
相关产品推荐
相关产品推荐

