...至少,如果“优雅的解决方法”的概念确实有值(value)的话!
以下是一些详细信息:
使用 CDI 和 JPA 时,您经常需要在 JSF 页面之一的 EL 表达式中访问 JPA 管理的 bean 之一,如下所示:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>An Everyday Facelet</title>
</h:head>
<h:body>
<h1>Behold, a jpa-managed bean property!</h1>
<h:outputText value="#{exampleEntity.name}" />
</h:body>
</html>
为了简单起见,我们假设此页面是 HTTP Post 的结果,并且客户端在上一页上输入了 name 属性的值。 bean 本身可以是从生产者方法检索的简单 jpa 实体,如下所示:
@Entity
public class ExampleEntity implements Serializable {
@Id
@Column
private Long entityId;
@Column
private String name;
public ExampleEntity() {
}
public void setEntityId(Long entityId) {
this.entityId = entityId;
}
public Long getEntityId() {
return entityId;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// Some method in another class
public class AnotherClass {
@Produces
@RequestScoped
@Named("exampleEntity")
public ExampleEntity getExampleEntity() {
return new ExampleEntity();
}
}
好吧,让我们编译吧。 datanucleus JPA 实现的一个重要方面是有一个后编译步骤。您需要在编译的 jpa 类上运行字节码增强器,所以我们假设我们这样做了。当尝试启动您的 servlet 容器(即 Tomcat、Jetty)时,您将在启动 Weld 时遇到异常(如果您实际上尝试将此 bean 注入(inject)某处,则会出现不止一个异常)。假设我的类位于“testapp”包中,异常(exception)情况如下:
org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001437 Normal scoped bean class testapp.ExampleEntity is not proxyable because the type is final or it contains a final method public final java.lang.Object testapp.ExampleEntity.jdoGetObjectId() - Managed Bean [class testapp.ExampleEntity] with qualifiers [@Any @Default @Named].
公共(public)最终java.lang.Object jdoGetObjectId()?我既没有定义也没有声明其中之一,好的网络服务器。你肯定搞错了!
事实证明,datanucleus 增强器必须将这个 final方法添加到我编译的类中。如果我不运行网络服务器,它就会正常启动,尽管我认为我的数据实际上不会持久存在。经过一些研究,我在 Weld 文档中找到了问题的解释:http://docs.jboss.org/weld/reference/1.1.5.Final/en-US/html/injection.html#d0e1429
简而言之,命名 bean 似乎不能有 final方法。根据那里的一些建议,我找到了一个可行的解决方案。我为 jpa bean 声明一个接口(interface),并创建一个生成器方法来获取具体类(实际上是 datanucleus 增强类,但 Weld 不知道)。这是附加代码:
public interface ExampleEntity {
public Long getEntityId();
public void setEntityId(Long entityId);
public String getName();
public void setName(String name);
}
现在,ExampleEntity 纯粹是一个接口(interface),我创建了一个 JPA 管理的实现,无需 CDI 注释
@Entity
public class ExampleEntityImpl implements ExampleEntity, Serializable {
// Same as the ExampleEntity class above
}
最后,也许在某个 Controller 中,我定义了一个用于获取ExampleEntities的生产者方法
@Named("exampleEntityController")
@RequestScoped
public class ExampleEntityController {
private ExampleEntity newExampleEntity;
public ExampleEntityController() {
newExampleEntity = new ExampleEntityImpl();
}
// The producer method
@Produces
@RequestScoped
@Named("exampleEntity")
public ExampleEntity getExampleEntity() {
return newExampleEntity;
}
// Other stuff, because surely a controller does more than just this, right?
}
而且...这有效。 Web 服务器启动,没有异常(exception)。在该示例 Facelet 中正确检索了名称属性(假设数据已在前一页中输入)。然而,为每个 jpa 管理的 bean 制作一次性接口(interface)和生产者方法是相当多余和丑陋的。有更好的解决方案吗? Datanucleus 是这个项目的要求,所以我不能简单地使用其他东西。如果有一个更“优雅的解决方法”,我想它涉及一种更好的方法让 Weld 让我拥有一个我永远不会在我命名的 bean 中使用的 final方法。
编辑:感谢您的回复/更正,但我正在寻找一种方法来避免为每个 jpa bean 创建接口(interface)。我已经从示例中删除了混合 JPA/CDI 注释,但 Web 服务器仍然抛出相同的异常。不过,我可以看到,无论哪种方式,我都需要一个生产者方法。
最佳答案
您不应为您的实体使用 CDI。
- 实体由您使用
new
运算符创建 - CDI bean 由容器创建,以便它可以处理注入(inject)和初始化
关于jakarta-ee - Weld (CDI) 和 Datanucleus (JPA) 效果不佳,有优雅的解决方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9985031/