@ApplicationScoped CDI bean和@PersistenceContext - 这样安全吗?

wrs*_*der 27 jpa java-ee cdi java-ee-6

用CDI做这样的事情是否安全?

@Named
@ApplicationScoped
public class DAO {

   @PersistenceContext
   private EntityManager entityManager;

}
Run Code Online (Sandbox Code Playgroud)

我知道它EntityManager本身不是线程安全的,因此不应该在共享的全局上下文中使用@ApplicationScoped.但是,由于注入的对象@PersistenceContext实际上是底层的线程感知包装EntityManager,这是否可以使这个好?

我已经看过关于这个主题的其他帖子,但未能找到这个具体案例的权威答案.例如:

Java CDI @PersistenceContext和线程安全

例如,看起来使用它是安全的@Stateless- 但我不确定这是因为工作方式@Stateless,还是因为@PersistenceContext它本身固有的东西.

编辑 我的困惑的来源是@PersistenceContext注入的EntityManager包装器似乎知道当前的线程,以便弄清楚是否已经有一个正在进行的事务.所以也许我将线程感知与线程安全混淆,它们是两个不同的东西.

Arj*_*jms 25

我很确定在这种情况下,CDI不会为实体管理器创建上下文代理.毕竟,它的范围是什么?您可能需要类似于假设@ThreadScoped或公正的东西@RequestScoped,但@PersistenceContext不是CDI注释,CDI不会修改其语义.

所以这里发生的是Java EE 6平台"托管bean"注入,类似于在Servlet中注入实体管理器.这两种情况都会为您提供一个直接使用的线程安全实例.

例如,看起来与@Stateless一起使用是安全的 - 但是我不确定这是因为@Stateless的工作方式,还是因为@PersistenceContext本身固有的东西.

这是因为工作方式@Stateless.对无状态bean上的方法的每个请求(调用)都由容器路由到唯一的实例.容器保证在同一个bean中没有两个线程处于活动状态.

使用CDI,您可以通过将实体管理器封装在请求范围的bean中并将其注入到应用程序作用域中来为每个请求获得类似的效果:

import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@RequestScoped
public class EntityManagerProvider {

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

}
Run Code Online (Sandbox Code Playgroud)

将此注入到先前注入实体管理器的bean中:

@Named
@ApplicationScoped
public class DAO {

   @Inject
   private EntityManagerProvider entityManagerProvider;

}
Run Code Online (Sandbox Code Playgroud)

这将为每个请求提供一个唯一的实体管理器.您也可以轻松将其转换为生产者方法,因此您不必调用getEntityManager()注入的提供程序.