@Version is incremented when a null attribute is set, resulting in an optimistic lock

Hello,

After a migration from version 3.6.10.Final to 5.4.27.Final, we observe a new behaviour concerning the treatment of null values when setting an attribute.

Is it on purpose that when setting an @Entity java object attribute to null that the @Version of this object is incremented?

This leads to an optimistic locking exception.

OurObject ourObject = ourObjectService.findById(id);
// ourObject version is 0

ourObject.setAttribute("Not null value");
ourObjectService.findById(id)
// ourObject version is still 0 (no problem)

ourObject.setAttribute(null)
ourObjectService.findById(id)
// ourObject version has been incremented to 1 ("problem")

// as the @Version was incremented later when trying to flush the change in the database, an optimistic lock exception occurs.
@Entity
public class OurObject implements Serializable {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	protected Long id; 

	@Version	
	protected long version;

	protected String attribute;

       //getters and setters

}

Thank you very much for every clarification.

Of course, every change to an attribute will cause an optimistic version increase. If you want to exclude certain attributes from optimistic locking, you can annotate them with @org.hibernate.annotations.OptimisticLock(exclude = true)

Hi Beikov
Thanks a lot for the clarification, yes by diving into the topic it appears that it should cause an optimistic version increase when an attribute is changed and this same entity is later retrieved from the database without saving the previous state.

What happened was that when we were in Hibernate 3.6.10.Final, dirty checking was not triggered for some reason I still can’t figure out, so the optimistic version was not increased (which was a problem).
After updating the Hibernate version, dirty checking is triggered correctly every time, resulting in an optimistic version increase (which is the desired behaviour).

What bothers me is that in our old code base with Hibernate version 3.6.10.Final, neither dirty checking nor updating was triggered by the application code, but in a test, they are indeed triggered as the log below shows:

//Test log, in "production" AbstractEntityPersister is not called in this case
[2022/07/21 18:15:38.295][main][INFO ] (OurObjectTest.java:setUp:72) OurObject version before setting an attribute: 0
[2022/07/21 18:15:38.316][main][TRACE] (AbstractEntityPersister.java:logDirtyProperties:3464) our.package.domain.entity.OurObject.attribute is dirty
[2022/07/21 18:15:38.319][main][TRACE] (AbstractEntityPersister.java:update:2527) Updating entity: [our.package.domain.entity.Sct#1]
[2022/07/21 18:15:38.319][main][TRACE] (AbstractEntityPersister.java:update:2529) Existing version: 0 -> New version: 1
[2022/07/21 18:15:38.320][main][TRACE] (AbstractEntityPersister.java:dehydrate:2179) Dehydrating entity: [our.package.domain.entity.Sct#1]
[2022/07/21 18:15:38.343][main][TRACE] (AbstractEntityPersister.java:insert:2392) Inserting entity: [other.package.domain.entity.EntityAuditLine#component[entityAuditClassName,objectId,attribute, version]{entityAuditClassName=OurObject, objectId=1, attribute="some value", version=1}]
[2022/07/21 18:15:38.343][main][TRACE] (AbstractEntityPersister.java:dehydrate:2179) Dehydrating entity: [other.package.domain.entity.EntityAuditLine#component[entityAuditClassName,objectId,attribute, version]{entityAuditClassName=OurObject, objectId=1, attribute="some value", version=1}]
[2022/07/21 18:15:38.349][main][INFO ] ((OurObjectTest.java:setUp:72) .java:tearDown:79) OurObject version after setting an attribute: 1

In the application code (multithreaded and with many transactions), the AbstractEntityPersister methods are not triggered. I don’t know why yet, maybe because of the activity load that Hibernate has to handle or probably because of incorrect configurations on our part.

With the new code of the application with Hibernate version 5.4.27.Final, the dirty checking is triggered well (the audit doesn’t appear, maybe I need to adjust the log).

//"New production" dirty checking well triggered every time
[2022/07/21 22:12:23.569][pool-17-thread-3][INFO ] (OurObjectServiceImpl.java:createOurObject:143) OurObject version before setting an attribute: 0
[2022/07/21 22:12:23.569][pool-17-thread-3][TRACE] (AbstractEntityPersister.java:logDirtyProperties:4722) our.package.domain.entity.OurObject.attribute is dirty
[2022/07/21 22:12:23.570][pool-17-thread-3][TRACE] (DefaultFlushEntityEventListener.java:logDirtyProperties:700) Found dirty properties [[our.package.domain.entity.OurObject.attribute#287144]] : [attribute]
[2022/07/21 22:12:23.570][pool-17-thread-3][TRACE] (DefaultFlushEntityEventListener.java:scheduleUpdate:294) Updating entity: [our.package.domain.entity.OurObject.attribute#287144]
[2022/07/21 22:12:23.577][pool-17-thread-3][INFO ] (OurObjectServiceImpl.java:createOurObject:146) Sct version after setting an attribute: 1

Still diving into the subject, in any case, thank you for the clarification you have already given