Search code examples
springjpajakarta-eeweld

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


In Spring, I can inject an javax.persistence.EntityManager into a Spring bean using the annotation @javax.persistence.PersistenceContext, like this:

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

This is documented in the Spring docs in chapter 20.5.2 Implementing DAOs based on plain JPA.

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

In particular, is it possible to reuse the annotation @PersistenceContext for CDI (because existing code uses it with Spring) ?

As far as I understand: When using a Java EE container, the container will interpret the annotation and inject an EntityManager. Is that correct? And is there a way to get this to work using Weld, but without a Java EE container?


I tried to inject the class above into another class using Weld (in Tomcat, without Java EE). The injection takes place, so Weld correctly creates an instance of MyRepository, however the field MyRepository.entityManager is null, as if the annotation @PersistenceContext was ignored.

What is happening (or rather, not happening) here?


Solution

  • While MilkMaid's answer is a sound way to go, I would just add a few more behind-the-scenes hints.

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

    Short answer - there is. Weld can allow for injection of next to any object you wish to have injectable, however you need to mind who owns/manages this object. In your case, it is EntityManager which is stuff from JPA, so guess what - JPA manages the lifecycle of such object. Therefore you need to create a "wrapper" (in reality its a proxy) for such object which will be handled by CDI/Weld. And that what you need producer for.

    as if the annotation @PersistenceContext was ignored.

    Indeed, it is ignored. Here is a simplification of what happens. Normally, that is in EE container, Weld would make the injection happen but the real magic behind that is not a Weld core code, but rather an integration on the EE server side (who adopts Weld SPI to handle such annotations and turn them into beans which it then injects).

    Generally speaking, trying to handle 'EE stuff' outside of EE containers might get tricky as you come across a lot of integration which originally happens inside the container, but you now need to handle that yourself.

    Technically one could probably make the @PersistenceContext annotation work, maybe by writing a CDI extension. However, that is a nasty hack rather than anything else - one would be bending the EE-only annotation for SE usage. I would advise against it, but if you still want to go that way, the idea would be to basically make CDI think the @PersistanceContext is yet another @Inject plus provide the producer. This would mean a LOT of manual work, there is no in-built mechanism in CDI to handle that for you - this is normally the EE server's responsibility.