Java 11 上的 Spring 数据和 Eclipselink 的键 class 未找到 [ManagedType]

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

在我的项目中,我使用 Spring Data Lovelace、Spring 5.1.1 和 Eclipselink 2.7.3,通过 com.ethlo.persistence.tools:eclipselink-maven-plugin:2.7.1.1 插件进行静态编织。另外,我正在使用 OpenJDK 11。

构建工作正常,eclipselink-maven-plugin 已执行。它生成一个 persistence.xml 我觉得不错。

但是当我 运行 测试时,我得到

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(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

我已经试过了:

更新: 我调试了 eclipselink 初始化,它没有 "know" 我的实体 classes。在 MetadataProcessor#initPersistenceUnitClasses 中,它们出现在 classNames 列表中(因此 persistence.xml 被正确摄取)。 Eclipselink 然后遍历所有找到的 class 名称,并且(除其他外)尝试通过调用 PersistenceUnitProcessor.isEntity(..) 找到 @Entity 注释,后者又调用 candidateClass.isAnnotationPresent("javax.persistence.Entity") - returns 错误。

换句话说:潜在的问题似乎是 Eclipselink 在我的实体 classes 上看不到 @Entity 注释(存在, 当然).

更新二: 我通过将 <property name="eclipselink.logging.level" value="ALL"/> 插入 persistence.xml 来启用 eclipselink 日志记录并得到以下输出:

[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.

所以,那里没有新信息。

更新 3: 情节变得复杂:我调试了 eclipselink 如何构建其 class 元数据(它无法在其中找到 @Entity 注释)。它为此使用 ASM 而不是使用反射。更准确地说,它使用自己重新打包的版本(在本例中)ASM 6.2(重新打包为 org.eclipse.persistence:org.eclipse.persistence.asm:2.7.3)。在正确找到注释(在 ClassReader:626 中)后,它会尝试 Visit the NestedMembers attribute (ClassReader:651),但失败并显示 UnsupportedOperationException。 Eclipselink 捕获此异常并添加一个不包含注释信息的 "virtual metadata class" (MetadataAsmFactory:143)。

我发现一个 issue in Spring's issue tracker 似乎描述了完全相同的问题。他们的解决方案是启用实验性 ASM 7 支持。还不知道如何在 eclipselink 中做到这一点。

我的实体 classes 包含一个嵌套的 class(准确地说是枚举),这似乎触发了问题。

Eclipselink 使用 ASM 解析实体 classes 并生成元数据,该元数据反过来用于确定实体 class 是否实际上是一个实体(即携带 @Entity 注释).

当在我的实体 classes 中遇到 "nested member"(即嵌套枚举)时,ASM 6.2 抛出一个 UnsupportedOperationException,这使得 eclipselink 丢弃已经为此 [=36 收集的元数据=] 并生成不包含注释的虚拟元数据。因此错误。

有四种方法可以解决此问题:

  1. 暂时不要在实体 classes 中使用嵌套 classes
  2. 编译您自己的 eclipselink 版本,您在 eclipselink 的 MetadataAsmFactory$ClassMetadataVisitor 构造函数中将 ASM api 级别设置为 ASM7_EXPERIMENTAL。我没有对此进行测试,但根据 this 它应该可以解决问题。
  3. 使用较低的字节码级别(理论上 9 或 10 应该可以)。同样,我没有对此进行测试,但根据 this 它应该可以解决问题。
  4. 使用另一个 JPA 提供程序

显然,这已经在 eclipselink master 及其 2.7 分支中得到修复,因此应该会在下一个版本中提供修复。