Skip to content

06.2 — JPA 高级与优化

定位: JPA 深度使用——N+1 问题、关联映射、缓存机制 面试高频度: ⭐⭐⭐⭐⭐ 考查方式: N+1 问题定位与解决、关联映射策略、缓存机制

一、这是什么?为什么需要它?

JPA 虽然简化了数据访问,但错误的使用会导致性能问题(N+1)。本章深入 JPA 的优化技术和高级映射。

二、原理拆解

2.1 N+1 问题

java
@Entity
public class Post {
    @Id
    private Long id;
    
    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    private List<Comment> comments;
}

// N+1 查询
List<Post> posts = postRepository.findAll();          // 1 条查询
for (Post post : posts) {
    post.getComments().size();                         // N 条查询
}
// 总共 1+N 条 SQL

解决方式

方案代码特点
@EntityGraph@EntityGraph(attributePaths = "comments")声明式,不修改 JPQL
JOIN FETCH@Query("SELECT p FROM Post p LEFT JOIN FETCH p.comments")命令式,修改 JPQL
@BatchSize@BatchSize(size = 10)N次 -> ceil(N/10)次

@EntityGraph vs JOIN FETCH 区别

特性@EntityGraphJOIN FETCH
声明式/命令式声明式,不修改 JPQL命令式,修改 JPQL
复用现有查询可组合到已有 @Query需要重写查询
多个关联可指定多个 attributePaths多个 JOIN FETCH

2.2 关联映射

注解关系维护方默认 Fetch
@OneToOne一对一持有外键的一方EAGER
@OneToMany一对多多方(mappedBy)LAZY
@ManyToOne多对一多方持有外键EAGER(注意!)
@ManyToMany多对多任意一方,需 JoinTableLAZY

建议:对于 @ManyToOne,显式设置 fetch = FetchType.LAZY 避免 N+1。

2.3 JPA 缓存

一级缓存(Persistence Context):

  • 作用范围:每个 EntityManager / 每个事务
  • 自动管理:事务提交时同步 dirty 对象
  • 减少 SQL:同一事务内相同 ID 查询只发一次 SQL

二级缓存(Second-Level Cache):

  • 作用范围:整个 SessionFactory / 所有事务共享
  • 实现:需第三方(Ehcache、Redis)
  • 配置:@Cacheable / @Cache(usage = ...)
  • 场景:读多写少、变化不频繁的实体

三、面试视角

追问答案要点
N+1 问题产生的原因?遍历懒加载的关联集合时,每访问一次触发一次 SQL
@EntityGraph 和 JOIN FETCH 的区别?EntityGraph 声明式不修改 JPQL,JOIN FETCH 命令式
一级缓存何时被清空?事务提交/回滚、clear()、close() 时
@ManyToOne 的默认 Fetch 策略?EAGER!建议改为 LAZY

📚 相关链接

  • **Spring Data JPA** — Repository 基础知识
  • **事务管理** — JPA 事务管理
  • ← 返回 **返回 数据访问索引**

Knowledge4J — Java 知识库