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

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

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

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

如果 Hibernate 不使用 hashCode()/equals()来检查脏状态,我想我不应该使用 equals()/hashCode()来检查实体对象(不是值对象) ,但是我也担心同样的操作符(==)是不够的。

所以,问题是:

  1. Hibernate 如何知道对象的属性是否已更改?

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

    还有,

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

57156 次浏览

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

资料来源: 《 Hibernate in Action 》一书(附录 B: ORM 实施策略)

然而,注意 Hibernate 的脏检查是独立于方法 equals/hascode 的,这一点很重要。Hibernate 根本不查看这些方法(除非使用 java.util。设置,但是这与脏检查无关,只与 CollectionsAPI 有关)我前面提到的状态快照类似于一个值数组。将框架的这样一个核心方面留给开发人员是一个非常糟糕的决定(老实说,开发人员不应该关心脏检查)。不用说,equals/hascode 可以根据您的需要以多种方式实现。我建议你阅读引用的书,那里作者讨论了 equals/hascode 的实现策略。非常有洞察力的阅读。

Hibernate 执行逐个字段的检查来确定实体的肮脏性。

所以 hashCode/equals 根本不会出现。

实际上,由 Hibernate 执行的逐字段脏检查在性能方面可能相当昂贵。

因此它提供了类似 Strategy 或 Interceptor.findDirty ()的接口来处理同样的问题。

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

Hibernate 默认脏检查机制将使所有当前附加实体的所有映射属性与其初始加载时间值相匹配。

您可以在下图中更好地展示这一过程:

Default automatic dirty checking

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

脏检查是否也包括任何附加的 AttributeConverters?如果 java 对象中的值保持不变,但 AttributeConverter 逻辑发生了更改,并导致不同的数据库值,该怎么办。

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

对于新旧 AttributeConverter,java 对象保持不变,但是数据库值会因为新旧 AttributeConverter 配置而改变。

也许值得添加,因为这分散了我一段时间的注意力: 如果您在持久对象上使用 CustomType,则使用 equals进行脏检查。

这个堆栈来自于在 Hibernate,MyType 中自定义数据类型的 equals 方法中设置一个断点,然后触发一个事务并查看正在调用的 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)