我可以使用 CDI 和 @PersistenceContext 注入 JPA EntityManager,就像 Spring 一样吗?

Can I inject an JPA EntityManager using CDI and @PersistenceContext, like with Spring?

在Spring中,我可以注入一个javax.persistence.EntityManager 使用注释 @javax.persistence.PersistenceContext 进入 Spring bean,如下所示:

@Service
public class MyRepository {
    @PersistenceContext
    private EntityManager entityManager;
}

这在 20.5.2 Implementing DAOs based on plain JPA 章的 Spring 文档中有记录。

如果我没有使用 Java EE 容器,有没有办法使用 CDI(特别是焊接)来做到这一点?

特别是,是否可以为 CDI 重用注释 @PersistenceContext(因为现有代码将其与 Spring 一起使用)?

据我了解:当使用 Java EE 容器时,容器将解释注释并注入一个 EntityManager。那是对的吗?有没有办法在没有 Java EE 容器的情况下使用 Weld 使其工作?


我尝试使用 Weld(在 Tomcat 中,没有 Java EE)将上面的 class 注入另一个 class。发生注入,因此 Weld 正确地创建了 MyRepository 的实例,但是字段 MyRepository.entityManagernull,就好像注释 @PersistenceContext 被忽略了一样。

这里发生了什么(或者更确切地说,没有发生)?

你可以这样做: 创建 实体管理器工厂生产者

public class EntityManagerFactoryProducer {

    @Produces
    @ApplicationScoped
    public EntityManagerFactory create() {
        return Persistence.createEntityManagerFactory("PU");
    }

    public void destroy(@Disposes EntityManagerFactory factory) {
        factory.close();
    }

}

并创建 Entity Manager Producer

public class EntityManagerProducer {

    @Inject
    transient EntityManagerFactory emf;

    @Produces
    @RequestScoped
    public EntityManager create() {
        return emf.createEntityManager();
    }

    public void destroy(@Disposes EntityManager em) {
        em.close();
    }
}

然后你可以使用依赖注入(CDI会为每个请求创建一个em)

@Inject
EntityManager entityManager;

您必须在主方法中启动 CDI 上下文

public static void main(String[] args) throws IOException {
    Weld weld = new Weld();
    WeldContainer container = weld.initialize();
    Application application = container.instance().select(Application.class).get();
    application.run();
    weld.shutdown();
}

ps。如果你有不止一个 PU 使用 @Qualifier

虽然 MilkMaid 的回答是一个不错的选择,但我只想添加一些幕后提示。

Is there a way to do this using CDI (specifically, Weld) if I am not using a Java EE container?

简答 - 有。 Weld 可以允许在您希望可注入的任何对象旁边注入,但是您需要注意 who owns/manages 这个对象。在你的例子中,它是 EntityManager 这是来自 JPA 的东西,所以猜猜是什么 - JPA 管理此类对象的生命周期。因此,您需要为将由 CDI/Weld 处理的此类对象创建一个 "wrapper"(实际上它是一个代理)。这就是你需要 producer 的原因。

as if the annotation @PersistenceContext was ignored.

确实被忽略了。这是对所发生情况的简化。通常,在 EE 容器中,Weld 会使注入发生,但背后真正的魔力不是 Weld 核心代码,而是 EE 服务器端的集成(谁采用 Weld SPI 来处理此类注释并将它们转换为 bean然后注入)。

一般来说,尝试在 EE 容器之外处理 'EE stuff' 可能会很棘手,因为您会遇到很多最初发生在容器内部的集成,但现在您需要自己处理。

从技术上讲,可能可以使 @PersistenceContext 注释起作用,也许可以通过编写 CDI extension。然而,这是一个令人讨厌的 hack,而不是其他任何东西 - 人们会为了 SE 使用而弯曲 EE-only 注释。我会反对它,但如果你仍然想那样做,这个想法基本上是让 CDI 认为 @PersistanceContext 是另一个 @Inject 加上提供生产者。这将意味着大量的手动工作,CDI 中没有内置机制来为您处理 - 这通常是 EE 服务器的责任。