Spring Data JPA如何通过单条SELECT查询获取带关联类的实体
JPA关联查询问题解答
1. 该行为是否为JPA正常表现
是,这是JPA默认的N+1查询问题,属于预期内的正常行为:
- 默认
FetchType.LAZY策略下,关联属性只有在被实际访问时才会触发查询,就会出现查询1次主实体,访问N个主实体的关联属性触发N次额外查询的情况。 - 就算修改为
FetchType.EAGER,JPA默认采用的是SELECT抓取策略而非关联JOIN,只是把额外查询的触发时机提前到主查询结束后,仍然会产生N次额外查询。
2. 除Entity Graph之外的解决方法
还有以下4种常用方案可以解决N+1问题,实现单次或少量查询获取关联数据:
- JPQL FETCH JOIN:在自定义
@Query中显式指定关联抓取,会生成带JOIN的单条查询语句,示例:@Query("SELECT a FROM A a LEFT JOIN FETCH a.b") List<A> findAllWithB(); - 批量抓取优化:在关联属性上加
@BatchSize(size = 100)注解,会将N次单条查询合并为批量查询,把N+1次查询减少为ceil(N/批量大小)+1次,适合不需要每次都关联查询的场景:
也可以配置全局批量抓取参数,在@OneToMany(fetch = FetchType.LAZY, mappedBy = "abc") @BatchSize(size = 100) private List<B> b;application.properties添加:spring.jpa.properties.hibernate.default_batch_fetch_size=100 - Hibernate专属JOIN抓取注解:在关联属性上添加Hibernate专属的
@Fetch(FetchMode.JOIN)注解,强制每次查询A时都用JOIN语句加载关联的B,自动生效在所有查询方法上:@OneToMany(fetch = FetchType.LAZY, mappedBy = "abc") @Fetch(FetchMode.JOIN) private List<B> b; - 使用查询DSL/Specification动态构造查询:如果用动态查询,可以在构造Specification时显式指定fetch关联属性,灵活控制是否关联查询。
3. @EntityGraph全局生效的实现方法
Spring Data JPA从2.6版本开始支持在Repository接口上直接标注@EntityGraph,可以对接口内所有查询方法默认生效,但你使用的Spring Boot 2.5.1对应Spring Data JPA版本低于2.6,不支持该特性,有两个兼容方案:
- 方案1:升级Spring Boot版本到2.6.x及以上,直接在Repository接口上标注
@EntityGraph即可全局生效,示例:@EntityGraph(attributePaths = "b") public interface ARepository extends JpaRepository<A, Long> { // 接口内所有查询方法都会自动应用该EntityGraph } - 方案2:不升级版本的前提下,可以把通用查询方法提前在Repository中定义好并统一标注
@EntityGraph,业务代码复用这些预定义的方法即可,不需要重复标注。也可以自定义Repository基类,通过方法拦截统一给所有查询方法添加EntityGraph逻辑,不过实现复杂度更高。
内容的提问来源于stack exchange,提问作者Man Dee
相关产品推荐
相关产品推荐

