74

Is it using some kind of byte codes modification to the original classes?

Or, maybe Hibernate get the dirty state by compare the given object with previously persisted version?

I'm having a problem with hashCode() and equals() methods for complicated objects. I feel it would be very slow to compute hash code if the object has collection members, and cyclic references are also a problem.

If Hibernate won't use hashCode()/equals() to check the dirty state, I guess I should not use equals()/hashCode() for the entity object (not value object), but I'm also afraid if the same operator (==) is not enough.

So, the questions are:

  1. How does Hibernate know if a property of an object is changed?

  2. Do you suggest to override the hashCode()/equals() methods for complicated objects? What if they contains cyclic references?

    And, also,

  3. Would hashCode()/equals() with only the id field be enough?

Community
  • 1
  • 1
Lenik
  • 13,946
  • 17
  • 75
  • 103

6 Answers6

115

Hibernate uses a strategy called inspection, which is basically this: when an object is loaded from the database a snapshot of it is kept in memory. When the session is flushed Hibernate compares the stored snapshot with the current state. If they differ the object is marked as dirty and a suitable SQL command is enqueued. If the object is still transient then it is always dirty.

Source: book Hibernate in Action (appendix B: ORM implementation strategies)

It's important to notice however that Hibernate's dirty-checking is independent of the methods equals/hascode. Hibernate does not look at these methods at all (except when using java.util.Set's, but this is unrelated to dirty-checking, only to the Collections API) The state snapshot I mentioned earlier is something similar to an array of values. It would be a very bad decision to leave such a core aspect of the framework in the hands of developers (to be honest, developers should not care about dirty-checking). Needless to say that equals/hascode can be implemented in many ways according to your needs. I recommend you to read the cited book, there the author discuss equals/hascode implementation strategies. Very insightful reading.

alexander
  • 1,191
  • 2
  • 20
  • 40
Antonio
  • 2,406
  • 1
  • 16
  • 8
  • 4
    I think one part of question is how exactly comparison happen? I mean it is == check for each property with stored snapshot? or something else? – Mohammad Adnan May 11 '15 at 07:59
  • It`s not quite correct. If @Embeddable object is used within Collection inside @Entity than equals/hashcode should be implemented as it will be used during dirty check before flush entity to DB. See equality check for PersistenceSet check: https://github.com/hibernate/hibernate-orm/blob/5.1/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentSet.java#L95 – Mr Blowder Jul 11 '18 at 09:34
26

Hibernate default dirty checking mechanism will match all mapped properties of all currently attached entities against their initial loading-time values.

You can better visualize this process in the following diagram:

Default automatic dirty checking

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • Why the dirty checking need to compare all entities it cached? This is so inefficient, why not only compare the objects passed into session update from last flush? – jean Mar 23 '19 at 07:46
  • The update happens automatically. Session#update has not even a JPA equivalent, and is just for detached entities. Therefore, how do you think Hibernate can know if an entity has changed other than check if it was modified. Bytecode enhancement can speed it up a little bit, but if the dirty checking mechanism is your bottleneck, it means you have loaded way too many entities, which is the real performance problem. – Vlad Mihalcea Mar 23 '19 at 07:53
  • Object equality can not help? User implement equals method according to business logic. Then hibernate can use this equal method to find the corresponding snapshot in session cache? – jean Mar 23 '19 at 08:52
  • The load-time snapshot is an Object[]. The cached object is the same object reference, which is always equal to the latest entity state. – Vlad Mihalcea Mar 23 '19 at 13:16
8

Hibernate does a field-by-field checking to determine the dirtiness of an entity.

So hashCode/equals do not come into the picture at all.

Actually, the field-by-field dirty checking done by Hibernate can be quite costly in terms of performance.

So it provides interfaces like Strategy or Interceptor.findDirty() to handle the same.

Following post explains this in greater detail (alongwith some ideas for applications to optimize it fully): http://prismoskills.appspot.com/lessons/Hibernate/Chapter_20_-_Dirty_checking.jsp

user2250246
  • 3,807
  • 5
  • 43
  • 71
  • During the field-by-field checking, if the value of field is not identical(using == to check returns false), then equals is used to check whether the value is changed. See org.hibernate.internal.util.compare.EqualsHelper.equals(Object, Object) used in dirty check method invocation chain. – Popeye Nov 01 '16 at 05:12
0

Probably worth adding, as this distracted me for a while: if you are using a CustomType on your persistent object, equals is used for the dirty check.

This stack is from setting a breakpoint in the equals method of my custom data type in Hibernate, MyType, then triggering a transaction and seeing the equals being called.

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)
EvilElk
  • 1
  • 1
-1

does the dirty checking also involve any attached AttributeConverters? what if the value in the java object stays the same but the AttributeConverter logic is changed and does lead to different database values.

so read entity with old AttributeConverter config, write entity with new AttributeConverter config.

the java object stays the same for old and new AttributeConverter but the database values changes because of old and new AttributeConverter config.

thomasmey
  • 9
  • 2
-3

It is simple-- when you load/get entity object by id and then set its new field values by setter method and close session without calling update() method. then hibernate automatically update the changed value in the table without affecting other fields. and at the same time entity object is in dirty state.

vishal thakur
  • 609
  • 6
  • 7