繁体   English   中英

CDI:在“注入链”中生成正确的EntityManager的好方法

[英]CDI: Good way to produce correct EntityManager in an “Injection chain”

从使用PersistenceContext到使用@Injected DAO,我重写了许多servlet。

在JSF中,我可以在@Produces方法中获取对FacesContext的引用,并根据已登录用户返回正确的EM(或者如果已登录,则使用默认值,表明有效用户可用)。

当我必须为注入到不同servlet中的同一DAO生成不同的EM,并且要注入的EM取决于启动“注入链”的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事件机制 请求的作用域创建EM的生产器,以便每次使用新请求重新创建它。 然后,您可以使用特定的entityManager作为参数来触发事件,生产者将观察到该事件。 生产者收到事件后,它将存储EM并将其作为生产价值返回。

执行方案:

  1. 适当的EntityManager em被注入到servlet中
  2. 一个CDI事件emEvent被注入到servlet中
  3. @PostConstruct或在服务方法的开头,通过emEvent.fire(em)触发事件
  4. 具有请求范围的EM生产者在收到时观察EntityManager类型的事件,将其存储
  5. 所有DAO都只需要注入EntityManager
    • 生产者返回在观察到的事件中接收到的存储实例EntityManager
  6. 请记住 ,必须在事件触发后才注入DAO,因此,必须使用Instance动态注入依赖于DAO的servlet的所有依赖项,或者必须具有代理作用域(例如@SessionScoped @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;
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM