Hibernate如何检测实体对象的脏状态?

时间:2011-03-11 02:54:08

标签: java hibernate orm entity-relationship identity

是否使用某种字节码修改原始类?

或者,也许Hibernate通过将给定对象与先前持久化的版本进行比较来获得脏状态?

我遇到了复杂对象的hashCode()equals()方法的问题。如果对象具有集合成员,并且循环引用也是一个问题,我觉得计算哈希代码会很慢。

如果Hibernate不会使用hashCode() / equals()来检查脏状态,我想我不应该将equals() / hashCode()用于实体对象(不是值)对象),但我也害怕同一个运算符(==)是不够的。

所以,问题是:

  1. Hibernate如何知道对象的属性是否发生了变化?

  2. 您是否建议覆盖复杂对象的hashCode() / equals()方法?如果它们包含循环引用怎么办?

    而且,

  3. 只有hashCode()字段的equals() / id是否足够?

6 个答案:

答案 0 :(得分:92)

Hibernate使用一种名为 inspection 的策略,基本上就是这样:当从数据库加载一个对象时,它的快照会保存在内存中。刷新会话时,Hibernate会将存储的快照与当前状态进行比较。如果它们不同,则将对象标记为脏,并将合适的SQL命令排入队列。如果对象仍然是瞬态的,那么它总是很脏。

来源:书中的Hibernate in Action(附录B:ORM实施策略)

重要的是要注意Hibernate的脏检查与equals / hascode 方法无关。 Hibernate根本不看这些方法(除了使用java.util.Set时,但这与脏检查无关,只与Collections API无关)我之前提到的状态快照类似于值数组。将框架的这个核心方面留在开发人员手中是一个非常糟糕的决定(说实话,开发人员不应该关心脏检查)。不用说,equals / hascode可以根据您的需要以多种方式实现。我建议你阅读引用的书,作者在那里讨论equals / hascode实现策略。非常有见地的阅读。

答案 1 :(得分:18)

Hibernate default dirty checking mechanism将匹配所有当前附加实体的所有映射属性与其初始加载时间值。

您可以在下图中更好地可视化此过程:

Default automatic dirty checking

答案 2 :(得分:7)

Hibernate会逐个字段进行检查,以确定实体的肮脏程度。

所以hashCode / equals根本不会出现在图片中。

实际上,Hibernate完成的逐字段脏检查在性能方面可能非常昂贵。

因此它提供了类似Strategy或Interceptor.findDirty()的接口来处理相同的内容。

以下帖子更详细地解释了这一点(以及对优化完全应用的应用的一些想法):http://prismoskills.appspot.com/lessons/Hibernate/Chapter_20_-_Dirty_checking.jsp

答案 3 :(得分:0)

可能值得补充,因为这让我分心了一段时间:如果您在持久对象上使用 CustomType,则 equals 用于脏检查。

这个栈是从我在Hibernate中自定义数据类型的equals方法中设置断点,MyType,然后触发一个事务,看到equals被调用了。

equals:68, MyType (xxxxxx)
isEqual:105, CustomType (org.hibernate.type)
isSame:119, AbstractType (org.hibernate.type)
isDirty:79, AbstractType (org.hibernate.type)
isDirty:249, CustomType (org.hibernate.type)
findDirty:316, TypeHelper (org.hibernate.type)
findDirty:4622, AbstractEntityPersister (org.hibernate.persister.entity)
dirtyCheck:585, DefaultFlushEntityEventListener (org.hibernate.event.internal)
isUpdateNecessary:242, DefaultFlushEntityEventListener (org.hibernate.event.internal)
onFlushEntity:169, DefaultFlushEntityEventListener (org.hibernate.event.internal)
flushEntities:232, AbstractFlushingEventListener (org.hibernate.event.internal)
flushEverythingToExecutions:92, AbstractFlushingEventListener (org.hibernate.event.internal)
onAutoFlush:50, DefaultAutoFlushEventListener (org.hibernate.event.internal)
accept:-1, 765785095 (org.hibernate.internal.SessionImpl$$Lambda$1238)
fireEventOnEachListener:102, EventListenerGroupImpl (org.hibernate.event.service.internal)
autoFlushIfRequired:1327, SessionImpl (org.hibernate.internal)

答案 4 :(得分:-1)

脏检查是否还包含任何附加的AttributeConverters?如果java对象中的值保持不变但AttributeConverter逻辑已更改并导致不同的数据库值怎么办?

因此,读取具有旧AttributeConverter配置的实体,写入具有新AttributeConverter配置的实体。

对于新旧AttributeConverter,java对象保持不变,但由于新旧AttributeConverter配置,数据库值发生了变化。

答案 5 :(得分:-3)

很简单 - 当您按id加载/获取实体对象,然后通过setter方法和关闭会话设置其新字段值而不调用update()方法。然后,hibernate会自动更新表中更改的值,而不会影响其他字段。 同时实体对象处于脏状态