有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我有很多Servlet,我正在从使用PersistenceContext重写为使用@Injected DAO

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

当我必须为注入到不同Servlet中的相同DAO生成不同的EMs,并且要注入的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);
}

共 (1) 个答案

  1. # 1 楼答案

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

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

    执行模式:

    1. 适当的EntityManager em被注入servlet
    2. CDI事件emEvent被注入servlet
    3. 无论是在@PostConstruct中还是在服务方法的开始,通过emEvent.fire(em)触发事件
    4. 具有请求范围的EM producer观察EntityManager类型的事件,收到时存储EM
    5. 所有DAO只需要注入EntityManager
      • producer返回在观察到的事件中接收到的存储实例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;
      }
    }