Search code examples
javajpadependency-injectionjersey-2.0hk2

How do I properly configure an EntityManager in a jersey / hk2 application?


I have a jersey-2 / hk2 application which uses JPA persistence. The EntityManager is bound at startup like this

public MyApplication() {
    // ...
    register(new AbstractBinder() {
        @Override
        public void configure() {
          bindFactory(EmFactory.class)
            .to(EntityManager.class)
            .in(RequestScoped.class);
        }
    });
}

with the factory class being

public class EmFactory implements Factory<EntityManager> {

    private static final String PERSISTENCE_UNIT = "unit";

    private EntityManagerFactory emf;
    private CloseableService closeableService;

    @Inject
    public EmFactory(@Named(PERSISTENCE_UNIT) String persistenceUnit,
            CloseableService closeableService) {
        emf = Persistence.createEntityManagerFactory(persistenceUnit);
        this.closeableService = closeableService;
    }

    @Override
    public EntityManager provide() {
        final EntityManager entityManager = emf.createEntityManager();
        closeableService.add(new Closeable() {

            @Override
            public void close() throws IOException {
                if(entityManager.isOpen()) {
                    entityManager.close();
                }
            }
        });
        return entityManager;
    }

    @Override
    public void dispose(EntityManager entityManager) {
        if(entityManager.isOpen()) {
            entityManager.close();
        }
    }
}

this works but then for each request i get a warning in the logs about an EntityManager being already registered:

HHH000436: Entity manager factory name (unit) is already registered. \
  If entity manager will be clustered or passivated, specify a unique \
  value for property 'hibernate.ejb.entitymanager_factory_name'

What am I doing wrong? What is the proper way to initialize an EntityManager in a jersey-2 / hk2 application?


Solution

  • One option is to instead of creating a new EntityManagerFactory in the EMFactory (which is in a request scope), you could create a singleton factory for the EntityManagerFactory, then just inject the EntityManagerFactory into the EMFactory.

    public class EMFFactory implements Factory<EntityManagerFactory> {
        private final EntityManagerFactory emf;
        public EMFFactory (){
            emf = Persistence.createEntityManagerFactory(persistenceUnit);
        }
        public EntityManagerFactory provide() {
            return emf;
        }
        ...
    }
    
    public class EMFactory implements Factory<EntityManager> {
        private final EntityManager em;
    
        @Inject
        public EMFactory (EntityManagerFactory emf){
            em = emf.createEntityManager();
        }
        public EntityManager provide() {
            return em;
        }
        ...
    }
    

    Haven't tested this exact implementation out, but it should look something like this. I've used this pattern before.

    register(new AbstractBinder() {
        @Override
        public void configure() {
          bindFactory(EMFFactory.class).to(EntityManagerFactory.class).in(Singleton.class);
          bindFactory(EMFactory.class).to(EntityManager.class).in(RequestScoped.class);
        }
    });
    

    UPDATE

    One thing to note about the above example is that it doesn't clean up resources, i.e. the EntityManager should be close; it won't close itself. There is a dispose method in the Factory class that we need to override, but from my experience, this is never called by Jersey.

    What we can do is add the EntityManager to a [CloseableService][1]

    public class EMFactory implements Factory<EntityManager> {
        private final EntityManagerFactory emf;
        private final CloseableService closeService;
    
        @Inject
        public EMFactory (EntityManagerFactory emf, CloseableService closeService){
            this.emf = emf;
            this.closeService = closeService;
        }
        public EntityManager provide() {
            final EntityManager em = emf.createEntityManager();
            this.closeService.add(new Closeable(){
                @Override
                public void close() {
                    em.close();
                }
            });
            return em;
        }
        ...
    }
    

    This way the EntityManager is ensured to be closed at the end of the request.