服务器部署的持久性单元的类加载问题

Classloading Issue with server deployed persistence unit

我在 EAR 包中遇到问题,该包在 EJB 模块中包含服务器部署的持久性单元,在 WAR 模块中包含 Web 应用程序

EAR
 |--- persistence unit (EJB module) 
 |--- web app (WAR)
 ...

一切都编译并成功执行部署(在 WildFly 10 CR5 下)。正确部署了持久性单元并创建了模式(在开发过程中使用 Hibernate 模式生成)。

尽管如此,当我尝试坚持其中一个实体时

MyEntitiy e = new MyEntitiy();
e.setId(UUID.randomUUID().toString());
e.setName("name");
entityService.save(e);

我收到一个运行时错误,其根本原因是:

Caused by: java.lang.IllegalArgumentException: Can not set java.lang.String field x.y.z.MyEntity.id to x.y.z.MyEntity
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
    at java.lang.reflect.Field.get(Field.java:393)
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:39)

深入调试器,我找到了方法 sun.reflect.UnsafeFieldAccessorImpl.ensureObj,它通过方法 Class.isAssignableFrom 在 classes

之间执行检查
  1. Field 对象的 class (Field.getDeclaredClass())
  2. 要持久化的实体class

此检查 returns 错误,因为到 classes 已经加载了不同的 ClassLoaders(它们在逻辑上是相同的 class) .

如何在不更改项目的总体布局的情况下解决这个问题(即,将持久性单元保留为 EJB 模块以在各个模块之间共享)?

我相信,您可能在 ejb jar 和 war 文件中包含了 MyEntity class(或实体),因此它们会被两个 class 加载器加载。您可能需要从 war 文件中删除域实体并对其进行测试。

根据 jboss 文档,默认情况下,在 ejb jar 中定义的 classes 可用于 war 文件中的 classes。因此,它们将仅由 ejb classloader 加载一次,并且对于 war 文件中的 classes 也是 used/available。