NonIdentifierAttribute.isLazy() returns false when @Basic(fetch=FetchType.LAZY)

Description

When setting the enableLazyInitialization parameter of the hibernate-enhance-maven-plugin's configuration to true, the non-identifier attributes annotated with @Basic(fetch = FetchType.LAZY) will show as LazyPropertyInitializer.UNFETCHED_PROPERTY in EntityEntry.getLoadedState(), while AbstractEntityTuplizer.getPropertyValues(Object) will show null current values because NonIdentifierAttribute.isLazy() returns false.

This causes a problem in DefaultFlushEntityEventListener.dirtyCheck(FlushEntityEvent), where the two different representations are stored in the following two variables:

And then (when there is no findDirty() interceptor override, and the entity is not an instance of SelfDirtinessTracker, so the enableDirtyTracking parameter of the hibernate-enhance-maven-plugin configuration is false), the dirty check is performed by [AbstractEntityPersister.findDirty(Object[] currentState, Object[] previousState, Object entity, SessionImplementor session)|https://github.com/hibernate/hibernate-orm/blob/5.0.8/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java#L4079], and then forwarded to [TypeHelper.findDirty(NonIdentifierAttribute[] properties, Object[] currentState, Object[] previousState, boolean[][] includeColumns, boolean anyUninitializedProperties, SessionImplementor session)|https://github.com/hibernate/hibernate-orm/blob/5.0.8/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java#L282]. This checks if the current state holds LazyPropertyInitializer.UNFETCHED_PROPERTY, but it holds null instead (as described in the first paragraph). These two values are then compared in the appropriate Type subclass' isDirty(), isSame() and isEqual() methods, which (depending on the actual implementation) either return an incorrect dirty state (and then an unnecessary UPDATE is scheduled in DefaultFlushEntityEventListener.onFlushEntity(FlushEntityEvent)), or throw an exception (e.g. ClassCastException when comparing a primitive byte array to LazyPropertyInitializer.UNFETCHED_PROPERTY.

In my opinion, the problem lies in NonIdentifierAttribute.isLazy() returning false in AbstractEntityTuplizer.getPropertyValues(Object), causing the current state array to hold null instead of LazyPropertyInitializer.UNFETCHED_PROPERTY. These different values are also visible in [Interceptor.findDirty(Object, Serializable, Object[], Object[], String[], Type[])|https://github.com/hibernate/hibernate-orm/blob/5.0.8/hibernate-core/src/main/java/org/hibernate/Interceptor.java#L198]'s currentState and previousState arrays. (As far as I can tell, no Type subclasses are prepared to handle LazyPropertyInitializer.UNFETCHED_PROPERTY directly, so it shouldn't be propagated to them from TypeHelper.findDirty().)

The code path of the error that I've observed:

First I've observed this problem with a UserType field, but I could reproduce it with byte[] too.

Setting enableDirtyTracking to true is a possible workaround, as DefaultFlushEntityEventListener.dirtyCheck(FlushEntityEvent) uses a different code path in that case.

My original hibernate-enhance-maven-plugin configuration:

Environment

None

Assignee

Luis Barreiro

Reporter

Gábor Varga

Fix versions

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure