27

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

@Named
@ApplicationScoped
public class DAO {

   @PersistenceContext
   private EntityManager entityManager;

}

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

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

Java CDI @PersistenceContext 和线程安全

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

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

4

2 回答 2

26

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

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

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

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

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

}

将其注入到您之前注入实体管理器的 bean 中:

@Named
@ApplicationScoped
public class DAO {

   @Inject
   private EntityManagerProvider entityManagerProvider;

}

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

于 2012-12-15T14:47:31.707 回答
0

这种方法怎么样:

    @RequestScoped
    类 EntityManagerProducer {
    
        @PersistenceContext
        私有实体管理器实体管理器;
    
        @生产
        @RequestScoped
        公共实体管理器生产实体管理器(){
          返回这个实体管理器;
        }
    }

在任何范围的 CDI Beans 中:

@注入
私有实体管理器实体管理器;

应该在每个请求中创建实体管理器,当它通过一个它注入的 CDI bean 时创建一个新的实体管理器。在注入的 CDI Bean 中,您将获得一个在请求中使用的实体管理器。如果需要交易,实体经理将加入它。因此,您在每个请求中都会获得一个新的实体管理器,这意味着最后一切都会被清理,并且您不会遇到线程冲突。简单易行,不是吗?

于 2020-08-27T14:26:49.970 回答