Skip to main content
 首页 » 编程设计

jakarta-ee之@ApplicationScoped CDI bean 和 @PersistenceContext之这安全吗

2024年10月25日6bluestorm

用 CDI 做这样的事情安全吗?

@Named 
@ApplicationScoped 
public class DAO { 
 
   @PersistenceContext 
   private EntityManager entityManager; 
 
} 

我了解 EntityManager本身不是线程安全的,因此不应在像 @ApplicationScoped 这样的共享全局上下文中使用。 .但是,由于注入(inject)的对象带有 @PersistenceContext实际上是底层 EntityManager 周围的线程感知包装器,这样可以吗?

我已经看过有关该主题的其他帖子,但无法为这个特定案例找出权威答案。例如:

Java CDI @PersistenceContext and thread safety

看起来与 @Stateless 一起使用是安全的,例如 - 但我不确定这是否是因为 @Stateless 的方式有效,或者因为 @PersistenceContext 的内在原因本身。

编辑
我困惑的根源是 @PersistenceContext注入(inject) EntityManager wrapper 似乎知道当前线程,以便确定是否已经有正在进行的事务。所以也许我把线程意识和线程安全混淆了,它们是两个不同的东西。

请您参考如下方法:

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

所以这里发生的是 Java EE 6 平台“托管 bean”注入(inject),它类似于在 Servlet 中注入(inject)实体管理器。这两种情况都为您提供了一个不能直接使用线程安全的实例。

It looks like it's safe to use with @Stateless, for instance - but I'm not sure if that's because of the way @Stateless works, or because of something intrinsic to @PersistenceContext itself.



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

使用 CDI,您可以通过将实体管理器封装在请求范围的 bean 中并将其注入(inject)到应用程序范围的 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; 
    } 
 
} 

将其注入(inject)到您之前注入(inject)实体管理器的 bean 中:
@Named 
@ApplicationScoped 
public class DAO { 
 
   @Inject 
   private EntityManagerProvider entityManagerProvider; 
 
} 

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