我正在将许多 servlet 从使用 PersistenceContext 重写为使用 @Injected DAO。
在 JSF 中,我可以在 @Produces 方法中获取对 FacesContext 的引用,并根据登录用户返回正确的 EM(或者在登录后使用默认值,有效用户可用)。
当我必须为注入(inject)不同 servlet 的相同 DAO 生成不同的 EM,并且要注入(inject)的 EM 依赖于启动“注入(inject)链”的 Servlet 时,如何以干净的方式执行此操作?
预期结果:
Servlet 1 DaoA EntityM. x
+-----------+ +-----------+ +-----------+
| @Inject | Inject into| @Inject | Inject into| |
| DaoA daoA <-----+------+ E.M. em <------------+ |
| etc | | | //em x | | |
+-----------+ | +-----------+ +-----------+
| DaoB EntityM. x
| +-----------+ +-----------+
| | @Inject | Inject into| |
+------+ E.M. em <------------+ |
| //em x | | |
+-----------+ +-----------+
Servlet 2 DaoA EntityM. y
+-----------+ +-----------+ +-----------+
| @Inject | Inject into| @Inject | Inject into| |
| DaoA daoA <------------+ E.M. em <------------+ |
| | | //em y | | |
+-----------+ +-----------+ +-----------+
编辑:
我认为从技术上讲,我可以摆脱这样的事情,但是当 DAO 也以其他方式使用并且有许多 servlet 需要升级时,这是一个巨大的困惑:
//In Servlet 1
@PersistenceContext(unitName="x")
EntityManager em;
@Inject
DaoA daoA;
@Inject
DaoB daoB;
@Postconstruct
public void postConstruct() {
daoA.setEm(em);
daoB.setEm(em);
}
//In Servlet 2
@PersistenceContext(unitName="y")
EntityManage r em;
@Inject
DaoA daoA;
@Postconstruct
public void postConstruct() {
daoA.setEm(em);
}
最佳答案
我假设当您想在 servlet 中做出决定时,您的 DAO 应该在整个请求中使用相同的实体管理器,因为请求在 servlet 中开始和结束。换句话说,在提供 http 请求时,只应使用一个实体管理器。
在这种情况下,您可以使用内置请求范围和 CDI event mechanism 。为 EM 创建一个生产者,它是请求范围的,以便每次使用新请求时都会重新创建它。然后,您可以使用特定的entityManager作为参数触发一个事件,该事件由您的生产者观察到。当生产者收到事件时,它将存储 EM 并将其作为生产值返回。
执行架构:
- 将适当的
EntityManager em
注入(inject) servlet - 将 CDI 事件
emEvent
注入(inject) servlet - 在
@PostConstruct
中或在服务方法的开头,通过emEvent.fire(em)
触发事件 - 具有请求范围的 EM 生产者观察 EntityManager 类型的事件,收到后存储 em
- 所有 DAO 只需注入(inject)
EntityManager
- 生产者返回在观察到的事件中接收的存储实例
EntityManager
- 生产者返回在观察到的事件中接收的存储实例
- 记住,您必须仅在事件触发后注入(inject) DAO,因此 servlet 的所有依赖项(依赖于 DAO)必须使用 Instance 动态注入(inject)。 ,或者必须具有代理范围(例如
@RequestScoped
、@SessionScoped
)。否则,实体管理器的生产者将在接收到任何事件之前被调用。但我相信这也适用于您问题中的简单解决方案。
代码示例:
//In Servlet 1
@PersistenceContext(unitName="x")
EntityManager em;
@Inject
Event<EntityManager> emEvent;
@Inject
Instance<DaoA> daoAInstance;
@Postconstruct
public void postConstruct() {
emEvent.fire(em);
daoAInstance.get().find(...); /* at this point, proper EM will be injected into DaoA.
You should access daoA only after emEvent is fired*/
}
<小时/>
// in producer
@RequestScoped (producer will be recreated for every request)
public class DynamicEMProducer {
EntityManager em; /* not injected, but set in observer method.
You may inject a default em if you wish using @PersistenceContext */
// this is handler of event fired in the servlet
public void emChanged(@Observes EntityManager em) {
this.em = em;
}
@Produces
public EntityManager produce() {
return em;
}
}
关于java - CDI:在 "Injection chain"中生成正确的 EntityManager 的好方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33806448/