Hibernate 的 dirty checking(脏检查) 是其核心机制之一,用于在事务提交或 flush()
时自动检测实体对象的属性是否发生了变化,从而决定是否需要向数据库发出 UPDATE
语句。
Dirty Checking 的工作原理如下:
- 加载时保存快照:
当 Hibernate 从数据库加载一个实体对象时,它会为该对象创建一个快照(snapshot),保存当前字段的原始状态,通常是一个内部的Map
或数组结构。 - 对象状态变化:
当你在代码中对实体对象的属性进行修改(比如user.setName("Tom")
),Hibernate 不会立即执行 SQL 更新,而是等待事务flush()
(自动或手动触发)时统一处理。 - 触发 flush 操作:
在事务提交前(或显式调用session.flush()
时),Hibernate 会遍历所有持久化状态的实体对象,将当前对象的属性值与快照进行对比。 - 差异检测(dirty checking):
如果发现有字段值与快照不同,说明对象被“脏改动”了,Hibernate 就会生成UPDATE
语句,仅更新发生变化的字段(视配置而定)。 - 更新数据库:
检测到差异后,Hibernate 执行相应的 SQL 语句同步数据。
示例
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = session.get(User.class, 1);
user.setName("Tom"); // 修改了 name 字段
tx.commit(); // commit 时会触发 flush -> dirty checking -> 执行 UPDATE
session.close();
小结
步骤 | 动作 |
---|---|
1 | 加载实体,Hibernate 记录字段快照 |
2 | 修改实体属性,Hibernate 不立即更新数据库 |
3 | 事务提交或 flush() ,Hibernate 比较快照和当前状态 |
4 | 有变化则执行 SQL 更新 |
注意事项
- Hibernate 只能检测通过 setter 方法或字段直接修改的属性。
- 如果你使用了绕过 Hibernate 的手段修改(比如反射或直接修改底层结构),可能不会被检测到。
- 配置
@DynamicUpdate
可以让 Hibernate 只更新发生变化的字段,而不是所有字段。