Environment
Jmix: 3.0.999-SNAPSHOT
Description
After migrating to Jmix 3.0 (master / 3.0.0-M2), reading an audit attribute (createdBy, createdDate, lastModifiedBy, lastModifiedDate) on an entity loaded through DataManager throws IllegalStateException: Cannot get unfetched attribute [...] from detached object. In UI, this breaks opening any detail view for an existing entity: InstanceLoaderImpl.load → DataContextImpl.merge reads audit attributes and fails.
The root cause is in the metamodel: properties declared on a mapped superclass located two or more levels above the entity class lose their JPA flag — MetadataTools.isJpa(metaProperty) returns false for them. With the typical base-class hierarchy Customer extends StandardTenantEntity extends StandardEntity, all properties of StandardEntity (id, version, audit and soft-delete attributes) are affected, while tenant declared on StandardTenantEntity (one level up) and the entity's own properties remain correct.
Consequences chain:
- Default fetch plans (
_local, _base) skip non-JPA properties (FetchPlanRepositoryImpl.isPersistent → false), so audit attributes are no longer selected — on 2.8 the same app had them in _local and no fetch group was set on instances (full row was loaded).
FetchGroupManager explicitly re-adds the primary key and soft-delete properties to the EclipseLink fetch group, but nothing re-adds audit properties — the loaded instance gets EntityFetchGroup without them.
EntityStates.isLoaded(entity, "createdBy") returns true (non-JPA properties are considered always loaded), so DataContextImpl.mergeState reads the value and EclipseLink throws on the detached instance.
All involved Jmix classes (DataContextImpl.mergeState, EntityStates, load checkers, FetchGroupManager, JmixEntityFetchGroup, FetchPlanRepositoryImpl) are identical between release_2_8 and master — the regression is in the metamodel content, not in the consumers.
Steps to reproduce
- Take an entity with a two-level mapped superclass hierarchy (e.g. Jmix Bookstore
Customer extends StandardTenantEntity extends StandardEntity, audit attributes declared on StandardEntity).
- Load it:
Customer c = dataManager.load(Customer.class).id(id).one(); (any fetch plan, including none).
- Read an audit attribute:
c.getCreatedBy();
A pure-metamodel check reproduces it without touching the database:
MetaClass metaClass = metadata.getClass(Customer.class);
metadataTools.isJpa(metaClass.getProperty("createdBy")); // false on 3.0, must be true
Actual behavior
java.lang.IllegalStateException: Cannot get unfetched attribute [createdBy] from detached object io.jmix.bookstore.customer.Customer-... [detached].
at org.eclipse.persistence.internal.queries.EntityFetchGroup.onUnfetchedAttribute(...)
at io.jmix.eclipselink.impl.JmixEntityFetchGroup.onUnfetchedAttribute(...)
...
at io.jmix.bookstore.entity.StandardEntity.getCreatedBy(...)
at io.jmix.core.entity.BaseEntityEntry.getAttributeValue(...)
at io.jmix.flowui.model.impl.DataContextImpl.mergeState(DataContextImpl.java:272)
at io.jmix.flowui.model.impl.InstanceLoaderImpl.load(InstanceLoaderImpl.java:133)
Measured state on 3.0 for Customer:
metadataTools.isJpa(...): false for id, version, createdBy, createdDate, lastModifiedBy, lastModifiedDate, deletedBy, deletedDate (level-2 superclass); true for tenant (level-1) and own properties.
_local plan: [tenant, firstName, lastName, email].
- Instance fetch group:
EntityFetchGroup(){firstName, lastName, address, deletedDate, associatedRegion, orders, id, version, deletedBy, email, tenant} — audit attributes missing.
EntityStates.isLoaded(entity, "createdBy") = true while the actual read throws.
Same app on Jmix 2.8.0: _local contains all local properties including audit, instance fetch group is null (full row loaded), getCreatedBy() returns the value.
Expected behavior
Properties inherited from any depth of @MappedSuperclass hierarchy keep their JPA flag in the metamodel; audit attributes are loaded as in 2.8, and reading them on a DataManager-loaded instance returns the value instead of throwing.
Root cause hypothesis
The regression appears with the Dynamic Model change (#5272 / #5271, commit b17800bfe6 "Dynamic Model (changes in core)"), which introduced per-generation metadata session cloning (io.jmix.core.impl.metadata.MetadataSessionCloneSupport, MetadataSessionCloneMetaPropertyHandler). The runtime metamodel is a clone, and cloning appears to lose the store/JPA flag for properties inherited from mapped superclasses more than one level above the entity. MetaModelLoader itself is unchanged in this area.
A secondary observation: entities with soft delete appear unaffected for deletedBy/deletedDate only because FetchGroupManager explicitly re-adds soft-delete properties (and the PK) to every fetch group; audit properties have no such fallback. This explains why adding the SoftDelete trait masks the problem for those attributes.
Environment
Jmix: 3.0.999-SNAPSHOT
Description
After migrating to Jmix 3.0 (master / 3.0.0-M2), reading an audit attribute (
createdBy,createdDate,lastModifiedBy,lastModifiedDate) on an entity loaded throughDataManagerthrowsIllegalStateException: Cannot get unfetched attribute [...] from detached object. In UI, this breaks opening any detail view for an existing entity:InstanceLoaderImpl.load→DataContextImpl.mergereads audit attributes and fails.The root cause is in the metamodel: properties declared on a mapped superclass located two or more levels above the entity class lose their JPA flag —
MetadataTools.isJpa(metaProperty)returnsfalsefor them. With the typical base-class hierarchyCustomer extends StandardTenantEntity extends StandardEntity, all properties ofStandardEntity(id,version, audit and soft-delete attributes) are affected, whiletenantdeclared onStandardTenantEntity(one level up) and the entity's own properties remain correct.Consequences chain:
_local,_base) skip non-JPA properties (FetchPlanRepositoryImpl.isPersistent→ false), so audit attributes are no longer selected — on 2.8 the same app had them in_localand no fetch group was set on instances (full row was loaded).FetchGroupManagerexplicitly re-adds the primary key and soft-delete properties to the EclipseLink fetch group, but nothing re-adds audit properties — the loaded instance getsEntityFetchGroupwithout them.EntityStates.isLoaded(entity, "createdBy")returnstrue(non-JPA properties are considered always loaded), soDataContextImpl.mergeStatereads the value and EclipseLink throws on the detached instance.All involved Jmix classes (
DataContextImpl.mergeState,EntityStates, load checkers,FetchGroupManager,JmixEntityFetchGroup,FetchPlanRepositoryImpl) are identical betweenrelease_2_8andmaster— the regression is in the metamodel content, not in the consumers.Steps to reproduce
Customer extends StandardTenantEntity extends StandardEntity, audit attributes declared onStandardEntity).Customer c = dataManager.load(Customer.class).id(id).one();(any fetch plan, including none).c.getCreatedBy();A pure-metamodel check reproduces it without touching the database:
Actual behavior
Measured state on 3.0 for
Customer:metadataTools.isJpa(...):falseforid,version,createdBy,createdDate,lastModifiedBy,lastModifiedDate,deletedBy,deletedDate(level-2 superclass);truefortenant(level-1) and own properties._localplan:[tenant, firstName, lastName, email].EntityFetchGroup(){firstName, lastName, address, deletedDate, associatedRegion, orders, id, version, deletedBy, email, tenant}— audit attributes missing.EntityStates.isLoaded(entity, "createdBy")=truewhile the actual read throws.Same app on Jmix 2.8.0:
_localcontains all local properties including audit, instance fetch group isnull(full row loaded),getCreatedBy()returns the value.Expected behavior
Properties inherited from any depth of
@MappedSuperclasshierarchy keep their JPA flag in the metamodel; audit attributes are loaded as in 2.8, and reading them on aDataManager-loaded instance returns the value instead of throwing.Root cause hypothesis
The regression appears with the Dynamic Model change (#5272 / #5271, commit
b17800bfe6"Dynamic Model (changes in core)"), which introduced per-generation metadata session cloning (io.jmix.core.impl.metadata.MetadataSessionCloneSupport,MetadataSessionCloneMetaPropertyHandler). The runtime metamodel is a clone, and cloning appears to lose the store/JPA flag for properties inherited from mapped superclasses more than one level above the entity.MetaModelLoaderitself is unchanged in this area.A secondary observation: entities with soft delete appear unaffected for
deletedBy/deletedDateonly becauseFetchGroupManagerexplicitly re-adds soft-delete properties (and the PK) to every fetch group; audit properties have no such fallback. This explains why adding the SoftDelete trait masks the problem for those attributes.