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

sle*_*ske 3 spring jpa weld jakarta-ee

在 Spring 中,我可以javax.persistence.EntityManager 使用 annotation将 an 注入到 Spring bean 中@javax.persistence.PersistenceContext,如下所示:

@Service
public class MyRepository {
    @PersistenceContext
    private EntityManager entityManager;
}
Run Code Online (Sandbox Code Playgroud)

这在 Spring 文档的第20.5.2实现基于普通 JPA 的 DAO 中有所记录。

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

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

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


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

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

Mil*_*aid 5

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

public class EntityManagerFactoryProducer {

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

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

}
Run Code Online (Sandbox Code Playgroud)

并创建实体管理器生产者

public class EntityManagerProducer {

    @Inject
    transient EntityManagerFactory emf;

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

    public void destroy(@Disposes EntityManager em) {
        em.close();
    }
}
Run Code Online (Sandbox Code Playgroud)

那么你可以使用依赖注入(CDI 将为每个请求创建一个 em)

@Inject
EntityManager entityManager;
Run Code Online (Sandbox Code Playgroud)

您必须在主方法中启动 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();
}
Run Code Online (Sandbox Code Playgroud)

附:如果您有多个 PU,请使用 @Qualifier


Sil*_*rus 5

虽然 MilkMaid 的答案是一个不错的方法,但我只想添加一些更多的幕后提示。

如果我不使用 Java EE 容器,有没有办法使用 CDI(特别是 Weld)来执行此操作?

简短的回答 - 有。Weld 可以允许在您希望可注入的任何对象旁边注入,但是您需要注意谁拥有/管理该对象。在你的例子中,它是EntityManager来自 JPA 的东西,所以猜猜看 - JPA 管理此类对象的生命周期。因此,您需要为此类对象创建一个“包装器”(实际上它是一个代理),该对象将由 CDI/Weld 处理。这就是你需要制作人的原因。

就好像注释 @PersistenceContext 被忽略一样。

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

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

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