我对 Hibernate 的理解是,当对象从数据库加载时,它们会被添加到会话中。在不同的点,根据您的配置,会话被刷新。此时,修改后的对象被写入数据库。
Hibernate 如何决定哪些对象是“脏的”并且需要写入?
Hibernate 生成的代理会拦截对字段的分配,并将对象添加到 Session 的脏列表中吗?
还是 Hibernate 会查看 Session 中的每个对象并将其与对象的原始状态进行比较?
还是完全不同的东西?
Hibernate 确实/可以使用字节码生成 (CGLIB),以便在您调用 setter(或什至分配给字段 afaict)时立即知道字段是脏的。
这会立即将该字段/对象标记为脏,但不会减少在刷新期间需要进行脏检查的对象数量。它所做的只是影响org.hibernate.engine.EntityEntry.requiresDirtyCheck()
. 它仍然会进行逐个字段的比较以检查是否脏污。
我说以上内容是基于最近对源代码(3.2.6GA)的拖网,无论增加了什么可信度。兴趣点是:
SessionImpl.flush()
触发onFlush()
事件。SessionImpl.list()
autoFlushIfRequired()
触发onAutoFlush()
事件的调用。(在感兴趣的表格上)。也就是说,查询可以调用刷新。有趣的是,如果没有事务,则不会发生刷新。AbstractFlushingEventListener.flushEverythingToExecutions()
,最终(在其他有趣的位置)在flushEntities()
。source.getPersistenceContext().getEntityEntries()
) 调用中的每个实体DefaultFlushEntityEventListener.onFlushEntity()
。dirtyCheck()
. 该方法确实对 CGLIB 脏标志进行了一些优化,但我们仍然最终循环遍历每个实体。Hibernate 对加载到 Session 中的每个对象的状态进行快照。在刷新时,将 Session 中的每个对象与其对应的快照进行比较,以确定哪些是脏的。根据需要发出 SQL 语句,并更新快照以反映(现在干净的)会话对象的状态。
查看 org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck 会话中的每个元素都会通过与未触及的版本(来自缓存或来自数据库的版本)进行比较来确定它是否脏。
Hibernate 默认脏检查机制将遍历当前附加的实体并将所有属性与它们的初始加载时间值匹配。
您可以在下图中更好地可视化此过程:
这些答案是不完整的(充其量——我不是这里的专家)。如果你的会话中有一个 hib man 实体,你什么都不做,当你调用 save() 时你仍然可以获得更新。什么时候?当另一个会话在您的 load() 和 save() 之间更新该对象时。这是我的示例:即使客户端没有更改值,休眠也会设置脏标志(并发出更新)