hibernate
Hibernate 是什么
Hibernate 是一个完整 ORM 框架,它通过实体类和表结构的映射,让开发者以面向对象的方式操作数据库。相比 MyBatis,Hibernate 更强调对象状态管理、自动脏检查、级联关系和缓存能力。
如果要从零理解它为什么能自动更新、为什么会懒加载异常、为什么会出现 N+1,先看 Hibernate/JPA 核心全过程原理。这页总览负责建立概念,深度页负责解释完整执行过程。
核心概念
| 概念 | 说明 |
|---|---|
| Entity | 和数据库表映射的实体对象 |
| Session | 持久化上下文,负责对象状态跟踪 |
| Transaction | 事务边界 |
| Persistence Context | 一级缓存和脏检查发生的位置 |
| HQL/JPQL | 面向实体对象的查询语言 |
| Criteria | 类型安全、动态构造查询条件 |
实体状态流转
mermaid
stateDiagram-v2
[*] --> Transient: new 对象
Transient --> Persistent: save/persist
Persistent --> Detached: session close/clear
Detached --> Persistent: merge/update
Persistent --> Removed: remove/delete
Removed --> [*]: flush 到数据库实体对象常见状态:
- Transient:临时状态,只存在于内存,没有数据库记录。
- Persistent:持久化状态,被 Session 管理,修改后会被脏检查发现。
- Detached:游离状态,有数据库标识,但已经不被当前 Session 管理。
- Removed:删除状态,事务提交或 flush 后删除数据库记录。
Hibernate 执行更新的流程
mermaid
flowchart TD
A[查询实体] --> B[实体进入持久化上下文]
B --> C[业务代码修改字段]
C --> D[事务提交或 flush]
D --> E[脏检查比较快照]
E --> F{字段是否变化}
F -- 是 --> G[生成 update SQL]
F -- 否 --> H[不执行更新]
G --> I[同步到数据库]优点和成本
| 方面 | 说明 |
|---|---|
| 优点 | 减少重复 CRUD、对象关系建模自然、缓存和脏检查能力强 |
| 成本 | 学习曲线更高、SQL 不够直观、复杂查询容易产生性能问题 |
常见性能问题
- N+1 查询:查询列表后又逐条懒加载关联对象。
- 级联范围过大:保存一个对象时意外更新大量关联数据。
- 懒加载失效:Session 关闭后访问懒加载属性导致异常。
- 自动生成 SQL 不符合预期:索引使用差、关联过多。
- 大批量写入不清理上下文:一级缓存占用过多内存。
使用建议
- 实体关联不要为了“对象完整”而无限双向关联。
- 查询列表页时优先使用 DTO 投影,避免加载大对象图。
- 对关键 SQL 打开日志并配合执行计划分析。
- 批量导入、复杂报表、强 SQL 优化场景可以使用原生 SQL。
- 如果团队主要使用 Spring Data JPA,需要理解 Hibernate 是其常见底层实现。
深入学习路线
| 学习顺序 | 内容 | 目标 |
|---|---|---|
| 1 | Hibernate/JPA 核心全过程原理 | 理解持久化上下文、实体状态、脏检查、flush、懒加载和 N+1 |
| 2 | Hibernate 学习路线 | 建立 Hibernate 的整体学习路径 |
| 3 | JPA 基础 | 理解 JPA 规范和 EntityManager |
| 4 | Spring Data JPA | 理解 Repository 封装和查询方式 |
| 5 | Spring Data JPA 事务与分页 | 掌握事务边界、DTO 转换、分页 count 和深分页风险 |
代码 Demo:实体和脏检查
java
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
public void changeUsername(String username) {
this.username = username;
}
}java
@Service
public class HibernateUserService {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void changeUsername(Long id, String username) {
User user = entityManager.find(User.class, id);
user.changeUsername(username);
// 不需要手写 update,事务提交时 Hibernate 会脏检查并生成 SQL。
}
}这个 Demo 体现了 Hibernate 和 MyBatis 的差异:Hibernate 关注实体状态,MyBatis 更关注显式 SQL。
为什么 Hibernate 要看最终 SQL
Hibernate 的原理是根据实体状态、关联关系和持久化上下文生成 SQL。它提高了 CRUD 效率,但也可能因为懒加载、关联映射、级联配置生成超出预期的 SQL。
如果不看最终 SQL,容易出现:
- 列表查询触发 N+1。
- 修改一个实体导致级联更新过多数据。
- 事务外访问懒加载字段报错。
- 分页查询 count SQL 很慢。
所以 Hibernate 不是“不用关心 SQL”,而是“通过对象操作减少模板代码,同时必须关注生成 SQL 是否合理”。
