PrimaryKey lookup does not work with nested mapped super classes

  • Jmix-Framework: 2.8.2
  • Jmix Studio: 3.0.0-261
  • Idea 2026.1.3 Ultimate

Saving an entity instance which is designed by two nested mapped superclasses fails. The code looks like supporting it, but it actually does not return the ancestor of the ancestor.

  • appid_BaseEntity is a mapped super class entity and defines the primary key attribute, version etc.
  • appid_LinkedBaseEntity is a mapped super itself and extends appid_BaseEntity. It defines a foreign key to another entity
  • appid_DataEntity1 extends LinkedBaseEntity with further specific attributes

When creating a appid_DataEntity1 instance by using the DataManager without discard saving context, there is a reload attempt after saving. It tries to resolve the primary key attribute by meta classes.

java.lang.IllegalStateException: Entity appid_DataEntity1 has no primary key
	at io.jmix.data.impl.JpqlQueryBuilder.getPrimaryKeyProperty(JpqlQueryBuilder.java:391) ~[jmix-data-2.8.2.jar:na]
	at io.jmix.data.impl.JpqlQueryBuilder.buildResultQuery(JpqlQueryBuilder.java:223) ~[jmix-data-2.8.2.jar:na]
	at io.jmix.data.impl.JpqlQueryBuilder.getResultQueryString(JpqlQueryBuilder.java:171) ~[jmix-data-2.8.2.jar:na]
	at io.jmix.data.impl.JpqlQueryBuilder.getQuery(JpqlQueryBuilder.java:184) ~[jmix-data-2.8.2.jar:na]
	at io.jmix.eclipselink.impl.JpaDataStore.createQuery(JpaDataStore.java:603) ~[jmix-eclipselink-2.8.2.jar:na]
	at io.jmix.eclipselink.impl.JpaDataStore.loadOne(JpaDataStore.java:149) ~[jmix-eclipselink-2.8.2.jar:na]
	at io.jmix.core.datastore.AbstractDataStore.loadAllAfterSave(AbstractDataStore.java:443) ~[jmix-core-2.8.2.jar:na]
	at io.jmix.core.datastore.AbstractDataStore.save(AbstractDataStore.java:239) ~[jmix-core-2.8.2.jar:na]
	at io.jmix.eclipselink.impl.JpaDataStore.save(JpaDataStore.java:244) ~[jmix-eclipselink-2.8.2.jar:na]
	at io.jmix.core.impl.UnconstrainedDataManagerImpl.lambda$saveContextToStore$7(UnconstrainedDataManagerImpl.java:280) ~[jmix-core-2.8.2.jar:na]
	at io.micrometer.observation.Observation.observe(Observation.java:564) ~[micrometer-observation-1.15.11.jar:1.15.11]
	at io.jmix.core.impl.UnconstrainedDataManagerImpl.saveContextToStore(UnconstrainedDataManagerImpl.java:280) ~[jmix-core-2.8.2.jar:na]
	at io.jmix.core.impl.UnconstrainedDataManagerImpl.save(UnconstrainedDataManagerImpl.java:237) ~[jmix-core-2.8.2.jar:na]
	at io.jmix.core.impl.UnconstrainedDataManagerImpl.save(UnconstrainedDataManagerImpl.java:156) ~[jmix-core-2.8.2.jar:na]

I debugged it to MetadataTools line 246 (ancestor = ancestor.getAncestor();) returning null.

    @Nullable
    public String getPrimaryKeyName(MetaClass metaClass) {
        String pkProperty = (String) metaClass.getAnnotations().get(PRIMARY_KEY_ANN_NAME);
        if (pkProperty != null) {
            return pkProperty;
        } else {
            MetaClass ancestor = metaClass.getAncestor();
            while (ancestor != null) {
                pkProperty = (String) ancestor.getAnnotations().get(PRIMARY_KEY_ANN_NAME);
                if (pkProperty != null)
                    return pkProperty;
                ancestor = ancestor.getAncestor();
            }
        }
        return null;
    }

As some additional information, this is how the meta class are initialized:

  • appid_BaseEntity
    • ancestors = [ ] (0)
    • descendants = [ … ] (4)
  • appid_LinkedBaseEntity
    • ancestors = [ ] (0)
    • descendants = [ … ] (11)
  • appid_DataEntity1
    • ancestors = [ app_idLinkedBaseEntity, appid_BaseEntity ] (2)
    • descendants = [ ] (0)

I tried to force loading the model as a workaround in advance and a different order - without any success. I guess, I cannot influence and start avoid using nested mapped super classes.

I understand everybody is excited and focusing on Jmix 3.0 these days. Nevertheless, I hope 2.8.3 will be available soon - running production applications on 2.8.1 are waiting for issues to be fixed.

Hi,

Thanks for the detailed report. We tried to reproduce the issue by building a data model with two nested @MappedSuperclass levels (BaseEntity defining the @Id/@VersionLinkedBaseEntity with an additional reference → concrete @Entity), and saving an instance via DataManager with the default (non-discard) save context.

However, we could not reproduce the problem: MetadataTools.getPrimaryKeyName() correctly walks the full ancestor chain and returns the primary key, and the save + reload works fine.

Could you please share a minimal demo project that reproduces the issue? That would let us pinpoint the exact configuration that breaks the ancestor lookup and file a proper issue.

Thanks!

Regards,
Igor