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:

Attachments

1

Activity

Show:

Luis Barreiro July 12, 2016 at 8:36 PM

That would be

Former user July 12, 2016 at 1:59 AM

I've added a test case to reproduce this issue and pushed to 5.0, 5.1, and master branches.

The fix for this issue in 5.1 and master branches was tracked by a different jira (not sure which one).

Luis Barreiro July 12, 2016 at 12:38 AM

is a follow up.

Former user June 22, 2016 at 10:20 PM

I'm reopening this because it causes HHH-10865.

Former user June 10, 2016 at 11:46 PM

Fixed in 5.0 branch only as the bug only affected 5.0.

Fixed

Details

Assignee

Reporter

Labels

Components

Fix versions

Affects versions

Priority

Created February 22, 2016 at 11:10 AM
Updated August 1, 2016 at 5:08 AM
Resolved July 12, 2016 at 12:38 AM