【问题标题】:CDI: Good way to produce correct EntityManager in an "Injection chain"CDI:在“注入链”中生成正确 EntityManager 的好方法
【发布时间】:2015-11-19 14:19:17
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

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

    在这种情况下,您可以使用内置的请求范围和CDI event mechanism。为 EM 创建一个生产者,它是 request 范围的,以便每次使用新请求时都会重新创建它。然后,您可以使用特定 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,因此依赖于 DAO 的 servlet 的所有依赖项必须使用 Instance 动态注入,或者必须具有代理范围(例如@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;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-10-05
      • 1970-01-01
      • 2016-06-07
      • 2019-06-15
      • 1970-01-01
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 2014-03-04
      相关资源
      最近更新 更多