| The AbstractEntityTuplizer.getPropertyValues(Object) method only calls the actual getter in two cases:
- shouldGetAllProperties(Object) returned true, meaning BytecodeEnhancementMetadataPojoImpl.hasUnFetchedAttributes(Object) returned false (so when all attributes are already fetched;
- or the property is not lazy loading.
In all other cases, and most importantly when the entity has any uninitialized attributes, it will put LazyPropertyInitializer.UNFETCHED_PROPERTY values into the Object[] values array, even for attributes that were in fact fetched. Consequently, in the demo application attached to this ticket, when the Parent entity is updated, the DefaultFlushEntityEventListener.onFlushEntity(FlushEntityEvent) method will see the values array with two LazyPropertyInitializer.UNFETCHED_PROPERTY instances (one for each Set attribute). The method isUpdateNecessary(FlushEntityEvent, boolean) will return false because TypeHelper.findDirty(NonIdentifierAttribute[], Object[], Object[], boolean[][], boolean, SharedSessionContractImplementor) will compare currentState[i] to LazyPropertyInitializer.UNFETCHED_PROPERTY. This is why the collection is not updated. And then the FlushVisitor.processEntityPropertyValues(Object[], Type[]) method will not mark the collection entries as reached, because AbstractVisitor.includeProperty(Object[], int) will return false. And then Collections.processUnreachableCollection(PersistentCollection, SessionImplementor) will (eventually) issue the delete SQL command. This is why the association is deleted from the database. |