如何解决将代理的EntityManager注入资源时,出现IncompatibleClassChangeError
我正在尝试将EntityManager
注入jersey资源和相关的存储库服务中。这个想法是在REST请求的边界启动和提交事务,因此必须使用相同的实体管理器。为此,我使用an operation创建了一个自定义HK2范围@TransactionScope
。
该应用程序作为Java SE应用程序运行,球衣资源仅是其中的一部分。其他服务在后台运行,通过调度程序或JMS触发,它们也使用相同的作用域。 Java 11,最新版本的jersey,HK2,Eclipselink等。
定义了范围@Proxiable
:
@Scope
@Proxiable(proxyForSameScope = false)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TransactionScope { }
使用范围在根服务定位器中注册EntityManager
的创建:
bindFactory(EMFactory.class)
.to(EntityManager.class)
.in(TransactionScope.class);
使用EntityManager
的存储库服务为@Singleton
。这些提供围绕数据库实体的CRUD操作。多亏了代理注入,他们在相同的范围内获得了相同的实体管理器。
想法是:
- 在交易边界处,开始HK2操作。对于球衣REST请求,这是通过
ContainerRequestFilter
实现的;对于后台服务,是运行作业的调度程序,或者是接收JMS消息的消息侦听器。 - 球衣资源/计划的作业/消息处理器开始数据库事务
entityManager.getTransaction().begin()
,然后执行特定的逻辑。 - 已注入存储库服务,并为数据库实体提供CRUD操作。
- 交易已提交。
- HK2操作已关闭。
将实体管理器注入后台服务时,所有这些都可以正常工作。但是,一旦我尝试将其注入到球衣资源中,就会抛出IncompatibleClassChangeError
。
ERROR c.s.s.e.RuntimeExceptionMapper - Caught A MultiException has 2 exceptions. They are:
1. java.lang.IncompatibleClassChangeError: class javax.persistence.EntityManager_$$_jvsted_0 has interface javax.persistence.EntityManager as super class
2. java.lang.IllegalArgumentException: While attempting to create a Proxy for javax.persistence.EntityManager in scope com.skalio.rsem.TransactionScope an error occured while creating the proxy
at org.jvnet.hk2.internal.ProxyUtilities.generateProxy(ProxyUtilities.java:211)
研究此错误后,通常建议检查类路径上是否存在重复和冲突的库。我相信这里不是这种情况(至少我什么也找不到)。
我不能使用@RequestScoped
,因为它只能用于球衣资源,而不能用于后台服务。
有什么建议我可以做什么?
更新
我注意到以下工作原理:
-
@TransactionScope
被标记为@Unproxiable
- 通过
EntityManager
注入javax.inject.Provider
这对我来说可能是前进的方向,即使我发现它不是那么干净。开发人员可能会忘记必须使用提供者注入。
有关依赖项的更新
我已经研究了更多的依赖关系。给我带来一点惊喜:EclipseLink不使用javax.persistence:javax.persistence-api
,而是使用org.eclipse.persistence:jakarta.persistence
。无论如何,它仍然是包含javax.persistence
包的类路径上的唯一位置。
[INFO] +- org.eclipse.persistence:eclipselink:jar:2.7.7:compile
[INFO] | +- org.eclipse.persistence:jakarta.persistence:jar:2.2.3:compile
[INFO] | \- org.eclipse.persistence:commonj.sdo:jar:2.1.1:compile
[INFO] +- org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor:jar:2.7.7:provided
[INFO] | +- org.eclipse.persistence:org.eclipse.persistence.core:jar:2.7.7:provided
[INFO] | | \- org.eclipse.persistence:org.eclipse.persistence.asm:jar:2.7.7:provided
[INFO] | \- org.eclipse.persistence:org.eclipse.persistence.jpa:jar:2.7.7:provided
[INFO] | +- org.eclipse.persistence:org.eclipse.persistence.antlr:jar:2.7.7:provided
[INFO] | \- org.eclipse.persistence:org.eclipse.persistence.jpa.jpql:jar:2.7.7:provided
有关服务定位器的更新
由于仅部分代码位于jersey,其余部分位于“后台”,因此我尽可能在“根” ServiceLocator
中进行注册。为了提供球衣访问权限,我将定位器从球衣连接到了根定位器。
// configures the jersey container
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig(ServiceLocator parentLocator) {
// These services need to be registered into jersey's locator
register(new JerseyContainerBinder());
// bridge services into jersey
register(new ServiceLocatorBridge(parentLocator));
}
}
JerseyContainerBinder
当前为空,我可以使用它来扩展或覆盖根定位符绑定。 ServiceLocatorBridge
获取球衣的定位器并将其连接:ExtrasUtilities.bridgeServiceLocator(jerseyLocator,sourceLocator);
因此,这是在背景中注入某些东西与在球衣资源中注入东西之间的区别:它必须首先经过桥。不确定是否重要。啊,如果球衣人只允许重复使用现有的定位器...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。