Search code examples
javaspring-data-jpaspring-dataeclipselink

No [ManagedType] was found for the key class for Spring Data and Eclipselink on Java 11


In my project I use Spring Data Lovelace, Spring 5.1.1 and Eclipselink 2.7.3 with static weaving via the com.ethlo.persistence.tools:eclipselink-maven-plugin:2.7.1.1 plugin. Also, I am using OpenJDK 11.

The build works fine and the eclipselink-maven-plugin is executed. It generates a persistence.xml that looks fine to me.

But when I run the tests, I get a

Caused by: java.lang.IllegalArgumentException: No [ManagedType] was found for the key class [com.example.MyEntity] in the Metamodel - please verify that the [Managed] class was referenced in persistence.xml using a specific <class>com.example.MyEntity</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.
    at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.entityEmbeddableManagedTypeNotFound(MetamodelImpl.java:180)
    at org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:527)
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:74)
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:66)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:188)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:139)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:123)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:64)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:305)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297)
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:211)
    at org.springframework.data.util.Lazy.get(Lazy.java:94)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:119)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1804)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1741)
    ... 57 more

I already tried:

  • Using Java 8: This solves the problem, but I want to use Java 11.
  • Inserting <exclude-unlisted-classes>false</exclude-unlisted-classes> into the persistence.xml manually: makes no difference, but then again, this property is "Not applicable to Java SE persistence units" anyways
  • Setting the eclipselink.weaving jpa property to false like in the Spring Data Eclipselink example project: makes no difference

Update: I debugged into the eclipselink initialization and it does not "know" my entity classes. In MetadataProcessor#initPersistenceUnitClasses, they appear (so the persistence.xml is correctly ingested) in the classNames list. Eclipselink then iterates over all found class names and (among others) tries to find the @Entity annotation by calling PersistenceUnitProcessor.isEntity(..) which in turn calls candidateClass.isAnnotationPresent("javax.persistence.Entity") - and that returns false.

In other words: The underlying problem seems to be that Eclipselink can not see the @Entity annotation on my entity classes (which are present, of course).

Update 2: I enabled eclipselink logging by inserting <property name="eclipselink.logging.level" value="ALL"/> into the persistence.xml and got this output:

[EL Warning]: metamodel: 2018-10-17 08:15:13.449--The collection of metamodel types is empty. Model classes may not have been found during entity search for Java SE and some Java EE container managed persistence units.  Please verify that your entity classes are referenced in persistence.xml using either <class> elements or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element
[EL Warning]: metamodel: 2018-10-17 08:15:13.464--The collection of metamodel [ManagedType] types is empty. Model classes may not have been found during entity search for Java SE and some Java EE container managed persistence units.  Please verify that your entity classes are referenced in persistence.xml using either <class> elements or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.  The lookup on [class de.viaprinto.backoffice.elasticsearch.persistence.entity.ElasticsearchIndex] will return null.
[EL Warning]: metamodel: 2018-10-17 08:15:13.464--The collection of metamodel [ManagedType] types is empty. Model classes may not have been found during entity search for Java SE and some Java EE container managed persistence units.  Please verify that your entity classes are referenced in persistence.xml using either <class> elements or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.  The lookup on [class de.viaprinto.backoffice.elasticsearch.persistence.entity.ElasticsearchIndex] will return null.

So, no new information there.

Update 3: The plot thickens: I debugged into how eclipselink builds its class metadata (which it cannot find the @Entity annotation in). It uses ASM for that instead of using reflection. To be more precise, it uses its own repackaged version of (in this case) ASM 6.2 (repackaged as org.eclipse.persistence:org.eclipse.persistence.asm:2.7.3). After correctly finding the annotation (in ClassReader:626) it tries to Visit the NestedMembers attribute (ClassReader:651) and that fails with an UnsupportedOperationException. Eclipselink catches this exception and adds a "virtual metadata class" (MetadataAsmFactory:143) that contains no Annotation information.

I found an issue in Spring's issue tracker that seems to describe the exact same problem. Their solution was to enable experimental ASM 7 support. No idea yet how to do that in eclipselink.

My entity classes contain a nested class (an enum, to be precise) which seems to trigger the problem.


Solution

  • Eclipselink uses ASM to parse the entity classes and generate metadata which in turn gets used to decide whether an entity class is actually an entity (i.e. carries the @Entity annotation).

    When encountering the "nested member" (i.e. nested enum) in my entity classes, ASM 6.2 throws an UnsupportedOperationException which makes eclipselink discard the already collected metadata for this class and generate dummy metadata which contains no annotations. Hence the error.

    There are four ways to fix this:

    1. Do not use nested classes in entity classes for now
    2. Compile your own version of eclipselink where you set the ASM api level to ASM7_EXPERIMENTAL in eclipselink's MetadataAsmFactory$ClassMetadataVisitor constructor. I have not tested this but according to this it should fix the issue.
    3. Use a lower bytecode level (9 or 10 should work, theoretically). Again, I have not tested this but according to this it should fix the issue.
    4. Use another JPA provider

    Apparently, this has already been fixed in eclipselink master and their 2.7 branch, so the fix should be available in the next release.