Fly*_*ing 22 java jpa entitymanager cdi java-ee-7
我正在使用Java EE 7.我想知道将JPA EntityManager
注入应用程序范围的 CDI bean 的正确方法是什么.您不能只使用@PersistanceContext
注释注入它,因为EntityManager
实例不是线程安全的.假设我们希望EntityManager
在每个HTTP请求处理的开始时创建我们,并在处理HTTP请求后关闭它们.我想到了两个选择:
1.创建一个请求范围的CDI bean,它具有对a的引用EntityManager
,然后将bean注入到应用程序范围的CDI bean中.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@ApplicationScoped
public class ApplicationScopedBean {
@Inject
private RequestScopedBean requestScopedBean;
public void persistEntity(Object entity) {
requestScopedBean.getEntityManager().persist(entity);
}
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,EntityManager
将在创建时RequestScopedBean
创建,并在RequestScopedBean
销毁时关闭.现在我可以将注入移动到一些抽象类中以将其从中删除ApplicationScopedBean
.
2.创建一个生成实例的生产者,EntityManager
然后将EntityManager
实例注入到应用程序范围的CDI bean中.
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;
}
Run Code Online (Sandbox Code Playgroud)
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
@ApplicationScoped
public class ApplicationScopedBean {
@Inject
private EntityManager entityManager;
public void persistEntity(Object entity) {
entityManager.persist(entity);
}
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,我们还将EntityManager
创建一个每个HTTP请求创建的,但是关闭该请求EntityManager
呢?在处理HTTP请求后它是否也会关闭?我知道@PersistanceContext
注释注入容器管理EntityManager
.这意味着EntityManager
当客户端bean被销毁时将关闭.在这种情况下什么是客户端bean?它是什么ApplicationScopedBean
,在应用程序停止之前永远不会被销毁,或者它是EntityManagerProducer
什么?有什么建议吗?
我知道我可以使用无状态EJB,而不是应用范围的bean,然后就注射EntityManager
用@PersistanceContext
注释,但是这不是问题的关键:)
Mar*_*son 38
你的CDI制作人几乎是对的.唯一的问题是你应该使用生产者方法而不是生产者领域.
如果您使用焊接作为CDI容器(GlassFish的4.1和8.2.0 WildFly),那么你结合实例@Produces
,@PersistenceContext
并@RequestScoped
在生产现场部署过程中应该抛出此异常:
org.jboss.weld.exceptions.DefinitionException:WELD-001502:资源生成器字段[资源生成器字段[EntityManager],其中限定符[@Any @Default]声明为[[BackedAnnotatedField] @Produces @RequestScoped @PersistenceContext com.somepackage.EntityManagerProducer. entityManager]]必须是@Dependent作用域
事实证明,当使用producer字段查找Java EE资源时,容器不需要支持除@Dependent之外的任何其他范围.
CDI 1.2,第3.7节.资源:
容器不需要支持除@Dependent之外的范围的资源.便携式应用程序不应定义除@Dependent之外的任何范围的资源.
这句话都是关于生产者领域的.使用生产者方法查找资源是完全合法的:
public class EntityManagerProducer {
@PersistenceContext
private EntityManager em;
@Produces
@RequestScoped
public EntityManager getEntityManager() {
return em;
}
}
Run Code Online (Sandbox Code Playgroud)
首先,容器将实例化生产者,并将容器管理的实体管理器引用注入到em
字段中.然后容器将调用您的producer方法并将其返回的内容包装在请求范围的CDI代理中.此CDI代理是您的客户端代码在使用时获得的代码@Inject
.因为生成器类是@Dependent(默认),所以生成的任何其他CDI代理都不会共享基础容器管理的实体管理器引用.每当另一个请求想要实体管理器时,生成器类的新实例将被实例化,并且新的实体管理器引用将被注入到生成器中,而生成器又被包装在新的CDI代理中.
为了在技术上正确,em
允许将资源注入到字段中的基础和未命名容器重用旧的实体管理器(请参阅JPA 2.1规范中的脚注,"7.9.1容器责任",第357页).但到目前为止,我们尊重JPA所要求的编程模型.
在前面的示例中,如果标记EntityManagerProducer
@Dependent或@RequestScoped 则无关紧要.使用@Dependent在语义上更正确.但是如果你在生产者类上放置比请求范围更广的范围,你冒险将底层实体管理器引用暴露给许多线程,我们都知道这不是一件好事.底层实体管理器实现可能是线程本地对象,但是可移植应用程序不能依赖于实现细节.
CDI不知道如何关闭你放入请求绑定上下文的任何东西.更重要的是,容器管理的实体管理器不能被应用程序代码关闭.
JPA 2.1,"7.9.1容器责任"部分:
如果应用程序在容器管理的实体管理器上调用EntityManager.close,则容器必须抛出IllegalStateException.
不幸的是,许多人确实使用一种@Disposes
方法来关闭容器管理的实体管理器.当Oracle提供的官方Java EE 7教程以及CDI规范本身使用处理器关闭容器管理的实体管理器时,谁可以责怪他们.这是完全错误的,无论你把电话放在哪里,在处理器方法或其他地方,调用EntityManager.close()
都会抛出IllegalStateException
.通过将生产者类声明为a,Oracle示例是两者中最大的罪人@javax.inject.Singleton
.据我们了解,这种风险暴露了底层实体经理对许多不同线程的引用.
这里已经证明,通过错误地使用CDI生成器和处理器,1)非线程安全的实体管理器可能泄漏到许多线程,2)处理器没有效果; 让实体经理开放.发生了什么是容器吞没的IllegalStateException,没有留下任何痕迹(一个神秘的日志条目,说有一个"错误破坏一个实例").
通常,使用CDI查找容器管理的实体管理器并不是一个好主意.应用程序很可能只是使用@PersistenceContext
它并且对它感到满意.但是,在您的示例中,规则总是存在例外情况,CDI也可用于抽象EntityManagerFactory
处理应用程序管理的实体管理器的生命周期.
要全面了解如何获取容器管理的实体管理器以及如何使用CDI查找实体管理器,您可能需要阅读本文和此内容.
Pio*_*ski -1
你可以注入savely EntityManagerFactory,它是线程保存的
@PersistenceUnit(unitName = "myUnit")
private EntityManagerFactory entityManagerFactory;
Run Code Online (Sandbox Code Playgroud)
然后你可以从entityManagerFactory检索EntityManager。
归档时间: |
|
查看次数: |
19531 次 |
最近记录: |