Hibernate 深入
hibernate 节点;衔接 orm-jpa。
35 分钟
Hibernate 深入
orm-essentials 教了 @Entity 与 EntityManager 的入门用法;本课补 Session 生命周期、懒加载陷阱、N+1 与二级缓存 等 Hibernate 特有细节,让你在 spring-boot-essentials 里不再「注解会写、SQL 爆炸不懂」。
学习目标
- 理解 Session / EntityManager 与持久化上下文(Persistence Context)
- 识别并修复 N+1 查询与 LazyInitializationException
- 掌握
@OneToManyfetch 策略与join fetch的取舍
实体与关系映射
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<OrderItem> items = new ArrayList<>();
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}
}
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String sku;
private int qty;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
}
双向关联时由「拥有外键的一方」维护关系;mappedBy 侧只读映射。
N+1 与 join fetch
// 坏味道:查 1 条 Order,再懒加载 N 次 items → N+1
Order order = em.find(Order.class, 1L);
order.getItems().size();
// 修复:一次 JPQL 拉齐
List<Order> orders = em.createQuery(
"select distinct o from Order o join fetch o.items where o.code = :code",
Order.class)
.setParameter("code", "ORD-001")
.getResultList();
| 现象 | 原因 | 对策 |
|---|---|---|
| LazyInitializationException | Session 已关仍访问懒集合 | @Transactional 内完成访问,或 DTO 投影 |
| 日志里 1+N 条 SELECT | 默认 LAZY + 循环触达 | join fetch / @EntityGraph / batch size |
| distinct 重复行 | join 笛卡尔积 | JPQL 加 distinct,或改 DTO 查询 |
Session 与 Spring 事务
Hibernate 的 Session 在 JPA 下由 EntityManager 包装。Spring 的 在方法入口绑定 Session、出口 flush 并关闭——。