使用CDI在JavaEE应用程序中获取对EntityManager的引用
我正在使用JavaEE7。我想知道将JPA使用CDI在JavaEE应用程序中获取对EntityManager的引用,java,jpa,cdi,entitymanager,java-ee-7,Java,Jpa,Cdi,Entitymanager,Java Ee 7,我正在使用JavaEE7。我想知道将JPAEntityManager注入应用范围的cdibean的正确方法是什么。不能只使用@PersistanceContext注释注入它,因为EntityManager实例不是线程安全的。假设我们希望在每个HTTP请求处理开始时创建EntityManager,并在处理HTTP请求后关闭。我想到了两个选择: 一,。 创建一个请求范围的CDIBean,该CDIBean引用了EntityManager,然后将该bean注入到应用程序范围的CDIBean中 impor
EntityManager
注入应用范围的cdibean的正确方法是什么。不能只使用@PersistanceContext
注释注入它,因为EntityManager
实例不是线程安全的。假设我们希望在每个HTTP请求处理开始时创建EntityManager
,并在处理HTTP请求后关闭。我想到了两个选择:
一,。
创建一个请求范围的CDIBean,该CDIBean引用了EntityManager
,然后将该bean注入到应用程序范围的CDIBean中
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@RequestScoped
public class RequestScopedBean {
@PersistenceContext
private EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
}
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class EntityManagerProducer {
@PersistenceContext
@Produces
@RequestScoped
private EntityManager entityManager;
}
在本例中,创建
RequestScopedBean
时将创建EntityManager
,并在销毁RequestScopedBean
时关闭。现在,我可以将注入移动到某个抽象类,以将其从应用程序CopedBean
中删除
二,。
创建一个生成EntityManager
实例的生产者,然后将EntityManager
实例注入到应用程序范围的CDIBean中
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@RequestScoped
public class RequestScopedBean {
@PersistenceContext
private EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
}
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class EntityManagerProducer {
@PersistenceContext
@Produces
@RequestScoped
private EntityManager entityManager;
}
在本例中,我们还将有一个
EntityManager
,它是在每个HTTP请求中创建的,但是关闭EntityManager
呢?在处理HTTP请求后,它是否也会关闭?我知道,@PersistanceContext
注释注入了容器管理的EntityManager
。这意味着当客户机bean被销毁时,EntityManager
将被关闭。在这种情况下,客户端bean是什么?它是在应用程序停止之前永远不会被销毁的ApplicationScopedBean
,还是EntityManagerProducer
?有什么建议吗
我知道我可以使用无状态EJB而不是应用程序范围的bean,然后通过
@PersistanceContext
注释注入EntityManager
,但这不是重点:)您可以注入savely EntityManager Factory,它是线程保存
@PersistenceUnit(unitName = "myUnit")
private EntityManagerFactory entityManagerFactory;
然后您可以从EntityManager工厂检索EntityManager。您应该使用
@Dispose
注释关闭EntityManager
,如下例所示:
@ApplicationScoped
public class Resources {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
@Produces
@Default
@RequestScoped
public EntityManager create() {
return this.entityManagerFactory.createEntityManager();
}
public void dispose(@Disposes @Default EntityManager entityManager) {
if (entityManager.isOpen()) {
entityManager.close();
}
}
}
你和你的CDI制作人几乎是对的。唯一的问题是,您应该使用producer方法,而不是producer字段 如果您使用Weld作为CDI容器(GlassFish 4.1和WildFly 8.2.0),那么在producer字段上组合
@products
、@PersistenceContext
和@RequestScoped
的示例在部署期间应引发此异常:
org.jboss.weld.exceptions.DefinitionException:weld-001502:
资源生产者字段[资源生产者字段[EntityManager]与
限定符[@Any@Default]声明为[[BackedAnnotatedField]
@生成@RequestScope@PersistenceContext
com.somepackage.EntityManagerProducer.entityManager]]必须为
@从属作用域
事实证明,在使用producer字段查找JavaEE资源时,容器不需要支持@Dependent以外的任何其他作用域
CDI 1.2,第3.7节。资源:
容器不需要支持具有其他作用域的资源
而不是依赖。可移植应用程序不应定义资源
具有除@Dependent以外的任何作用域
这句话是关于生产者领域的。使用生产者方法查找资源是完全合法的:
public class EntityManagerProducer {
@PersistenceContext
private EntityManager em;
@Produces
@RequestScoped
public EntityManager getEntityManager() {
return em;
}
}
首先,容器将实例化生产者,容器管理的实体管理器引用将被注入em
字段。然后容器将调用producer方法,并将其返回的内容封装在请求范围的CDI代理中。此CDI代理是客户端代码在使用@Inject
时得到的。因为producer类是@Dependent(默认),所以生成的任何其他CDI代理都不会共享底层容器管理的实体管理器引用。每当另一个请求需要实体管理器时,producer类的一个新实例将被实例化,一个新的实体管理器引用将被注入到producer中,而producer又被包装在一个新的CDI代理中
从技术上讲,允许向字段em
中注入资源的基础容器和未命名容器重用旧的实体管理器(参见JPA 2.1规范中的脚注,第357页“7.9.1容器责任”一节)。但到目前为止,我们尊重JPA所要求的编程模型
在前面的示例中,将EntityManagerProducer
@Dependent或@requestscope标记为Dependent并不重要。使用@Dependent在语义上更为正确。但是,如果在producer类上设置了比请求范围更宽的范围,那么就有可能将底层实体管理器引用暴露给许多线程,我们都知道这不是一件好事。底层实体管理器实现可能是一个线程本地对象,但可移植应用程序不能依赖于实现细节
CDI不知道如何关闭您放入请求绑定上下文中的任何内容。最重要的是,应用程序代码不能关闭容器管理的实体管理器
JPA 2.1第7.9.1节“集装箱责任”:
如果应用程序
在容器管理的实体管理器上调用EntityManager.close
不幸的是,许多人确实使用@Disposes
方法来关闭容器管理的实体管理器。当Oracle提供的官员以及其自身使用处置器关闭容器管理的实体管理器时,谁又能责怪他们呢。这完全是错误的,对EntityManager.close()
的调用将抛出IllegalStateException
,无论您将该调用放在何处,放在disposer方法中还是其他方法中