Hibernate 学习路线
Hibernate 是 Java 生态中非常经典的 ORM 框架,也是 JPA 规范的常见实现。它的核心思想是把数据库表映射成 Java 对象,让开发者用对象的方式操作数据。
零基础学习 Hibernate 时,不要只背注解。重点要理解实体状态、持久化上下文、事务边界、懒加载和 N+1 查询问题。
详细全过程建议配合阅读 Hibernate/JPA 核心全过程原理,那里会把实体状态、脏检查、flush、commit、懒加载、N+1 和商业排查串成一条完整链路。
Hibernate 解决什么问题
如果直接使用 JDBC,开发者需要手动:
- 写 SQL。
- 设置参数。
- 执行查询。
- 遍历 ResultSet。
- 把每一列映射到 Java 对象。
- 管理连接和事务。
Hibernate 会自动处理对象和表之间的映射,并在事务提交时同步对象变化。
Hibernate 和 MyBatis 的区别
| 对比 | Hibernate | MyBatis |
|---|---|---|
| 关注点 | 对象模型和持久化上下文 | SQL 可控性 |
| SQL | 框架自动生成较多 | 开发者手写较多 |
| 学习重点 | 实体状态、关联、懒加载 | 参数映射、动态 SQL、结果映射 |
| 适合场景 | 领域模型清晰、CRUD 多 | SQL 复杂、报表多 |
| 风险 | N+1、懒加载、隐式 SQL | SQL 维护成本 |
实际项目里没有绝对优劣,要看团队习惯和业务复杂度。
学习路线
flowchart TD
A["实体映射"] --> B["主键和字段映射"]
B --> C["实体状态"]
C --> D["持久化上下文"]
D --> E["事务和脏检查"]
E --> F["关联关系"]
F --> G["懒加载和 N+1"]
G --> H["JPQL、Criteria、原生 SQL"]
H --> I["性能优化"]实体映射
一个简单实体:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name")
private String userName;
private Integer age;
}注解说明:
| 注解 | 作用 |
|---|---|
@Entity | 标记为实体类 |
@Table | 指定表名 |
@Id | 主键 |
@GeneratedValue | 主键生成策略 |
@Column | 字段和列映射 |
实体状态
Hibernate 中实体对象有状态。
flowchart TD
A["Transient 瞬时态"] --> B["Persistent 持久态"]
B --> C["Detached 游离态"]
B --> D["Removed 删除态"]| 状态 | 说明 |
|---|---|
| Transient | 新创建对象,未和数据库关联 |
| Persistent | 被持久化上下文管理 |
| Detached | 曾被管理,但 Session 已关闭 |
| Removed | 标记为删除 |
理解状态很重要,因为只有持久态对象才会被脏检查自动同步。
持久化上下文
持久化上下文可以理解成 Hibernate 的一级缓存和对象管理区。
它负责:
- 保存当前事务中加载的实体。
- 保证同一个主键只对应一个对象实例。
- 进行脏检查。
- 在 flush 时同步 SQL。
脏检查
脏检查 Dirty Checking 指 Hibernate 自动检测对象属性变化。
User user = entityManager.find(User.class, 1L);
user.setUserName("新名字");
// 不需要显式 update,事务提交时 Hibernate 会自动生成 update SQL这很方便,但也意味着你要清楚事务边界,避免无意中修改实体导致更新数据库。
关联关系
常见关联:
- 一对一
@OneToOne - 一对多
@OneToMany - 多对一
@ManyToOne - 多对多
@ManyToMany
建议:
- 不要为了对象完整而滥用双向关联。
- 多对多通常拆成中间实体更清晰。
- 列表查询不要直接加载大量关联集合。
懒加载和 N+1
懒加载表示关联数据在真正访问时才查询。
N+1 问题示例:
- 先查询 1 次文章列表。
- 遍历每篇文章时,再查询作者。
- 如果有 N 篇文章,就额外查询 N 次。
flowchart TD
A["查询文章列表 1 次"] --> B["遍历文章"]
B --> C["每篇文章查询作者"]
C --> D["总共 1 + N 次查询"]解决思路:
- 使用 fetch join。
- 使用 EntityGraph。
- 使用批量加载。
- 查询 DTO。
- 对列表接口控制分页。
Controller 不要直接返回实体
实体通常包含关联关系,直接返回可能导致:
- 懒加载异常。
- JSON 序列化循环引用。
- 暴露不该返回的字段。
- 触发额外 SQL。
建议转换 DTO。
使用建议
- 简单 CRUD 可以使用 Hibernate/JPA。
- 复杂报表查询可以使用原生 SQL 或 MyBatis。
- 事务边界放在 Service 层。
- 列表查询必须分页。
- 关注 SQL 日志,避免隐式 N+1。
本章小结
Hibernate 的重点不是注解本身,而是实体状态、持久化上下文、脏检查、关联关系和懒加载。学会看 Hibernate 生成的 SQL,才能真正用好它。
核心原理
Hibernate 的核心机制可以概括为:实体映射表结构,持久化上下文管理实体状态,事务提交时通过脏检查生成 SQL。
flowchart TD
A["Entity 映射表"] --> B["Persistence Context"]
B --> C["管理实体状态"]
C --> D["事务提交"]
D --> E["脏检查生成 SQL"]这解释了为什么 Hibernate 写 CRUD 很快,但复杂关联和性能问题必须看最终 SQL。
下一步阅读:Hibernate/JPA 核心全过程原理。
