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

时间:2015-11-19 14:19:18

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

我有许多servlet,我正在使用PersistenceContext重写使用@Injected DAO。

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

当我必须为注入不同servlet的相同DAO生成不同的EM并且要注入的EM依赖于​​启动“注入链”的Servlet时,如何以干净的方式执行此操作? / p>

预期结果:

  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);
}

1 个答案:

答案 0 :(得分:2)

我假设当你想在servlet中决定时,你的DAO应该在整个请求中使用相同的实体管理器,因为请求在servlet中开始和结束。换句话说,在提供http请求时,只应使用一个实体管理器。

在这种情况下,您可以使用内置请求范围和CDI event mechanism。为EM创建一个生产者,请求作用域,以便每次使用新请求重新创建它。然后,您可以使用特定的entityManager作为参数触发事件,生产者可以观察该事件。当生产者收到事件时,它将存储EM并将其作为生成值返回。

执行模式:

  1. 将适当的EntityManager em注入servlet
  2. 将CDI事件emEvent注入servlet
  3. @PostConstruct或在服务方式的开头,按emEvent.fire(em)点击事件
  4. 具有请求范围的EM生产者观察EntityManager类型的事件,当收到时,存储em
  5. 所有DAO只需要注入EntityManager
    • 生成器返回在观察到的事件中收到的存储实例EntityManager
  6. 记住,你必须在事件被触发后才注入DAO,因此必须使用Instance动态注入依赖于DAO的servlet的所有依赖项,或者必须具有代理范围(例如@RequestScoped@SessionScoped)。否则,在收到任何事件之前,将调用实体管理器的生产者。但我相信这也适用于你问题中的简单解决方案。
  7. 代码示例:

    //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;
      }
    }