java - CDI:在 "Injection chain"中生成正确的 EntityManager 的好方法

标签 java jakarta-ee servlets cdi java-ee-6

我正在将许多 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 并将其作为生产值返回。

执行架构:

  1. 将适当的 EntityManager em 注入(inject) servlet
  2. 将 CDI 事件 emEvent 注入(inject) servlet
  3. @PostConstruct 中或在服务方法的开头,通过 emEvent.fire(em) 触发事件
  4. 具有请求范围的 EM 生产者观察 EntityManager 类型的事件,收到后存储 em
  5. 所有 DAO 只需注入(inject) EntityManager
    • 生产者返回在观察到的事件中接收的存储实例EntityManager
  6. 记住,您必须仅在事件触发后注入(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/

相关文章:

java - 使用tomcat 8管理器部署后找不到WAR文件路径

java - 为什么在将重复项目添加到 Set 时不会出现错误?

java - 如果定义类未声明范围,CDI 生成器方法将不起作用

jakarta-ee - servletRequest是接口(interface)还是类?ServletRequest和HttpServletRequest有什么区别?

java.io.IOException : Error writing to server 异常

java - 为什么不调用 finalize?

java - ChronicleMap没有手动关闭,从Cleaner中清理

html - 说明 "Cache-Control" header 的用法

java - response.sendredirect() 改变方法类型

java - 阅读 Web 应用程序资源