当 Hibernate 刷新 Session 时,它如何确定会话中哪些对象是脏的?
题
我对 Hibernate 的理解是,当对象从数据库加载时,它们被添加到会话中。根据您的配置,会话会在不同的时间点被刷新。此时,修改的对象将被写入数据库。
Hibernate 如何决定哪些对象是“脏”对象并且需要写入?
Hibernate 生成的代理是否会拦截对字段的赋值,并将对象添加到会话中的脏列表中?
或者 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 对加载到会话中的每个对象的状态进行快照。在刷新时,会话中的每个对象都会与其相应的快照进行比较,以确定哪些对象是脏的。根据需要发出 SQL 语句,并更新快照以反映(现在干净的)Session 对象的状态。
看看org.hibernate.event.def.defaultflushentityeventlistener.dirtycheck会话中的每个元素都可以使用此方法来确定是否与未触摸的版本进行比较(一个来自缓存或数据库中的版本的版本一个)。
Hibernate默认的脏检查机制 将遍历当前附加的实体并将所有属性与其初始加载时间值进行匹配。
您可以通过下图更好地形象化此过程:
这些答案是不完整的(充其量——我不是这方面的专家)。如果您的会话中有一个 hib man 实体,您无需对其执行任何操作,当您对其调用 save() 时,您仍然可以获得发布的更新。什么时候?当另一个会话在您的 load() 和 save() 之间更新该对象时。这是我的例子: 即使客户端没有更改值,hibernate也会设置脏标志(并发出更新)