7

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?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
agnul
  • 12,608
  • 14
  • 63
  • 85
  • The hk2 part of this looks ok – jwells131313 Jan 21 '15 at 12:46
  • Yep, everything works fine, except I get that warning in the logs for each request. I suspect the EntityManagerFactory field above should be a singleton, with the EmFactory still being resource scoped. – agnul Jan 22 '15 at 21:45
  • 1
    Instead of creating a new emf in the em factory, have you tried to create a singleton factory for the emf, and inject the emf to the em factory? – Paul Samsotha Jan 23 '15 at 11:55
  • Adds more boilerplate but works nicely, you could make that an answer. – agnul Jan 23 '15 at 14:43

1 Answers1

14

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.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • i tried the implementation just with the EMFactory as i don't need the EMFFactory. Then i tried to inject it into the injectionresolver implementation together with httpServletRequest but i get the MultiExcpetion stack because HK2 cannot revolve the Injectionpoint. – Alex Oct 14 '15 at 12:43
  • @Alex Maybe a scoping issue. Did you make the EMFactory RequestScoped? – Paul Samsotha Oct 14 '15 at 12:53
  • yes: bindFactory(EMFactory.class).to(EntityManager.class).in(RequestScoped.class); – Alex Oct 14 '15 at 12:56
  • @Alex That's problem because the InjectionResolver is a singleton. The HttpServletRequest works because Jersey injects a proxy. It's not the actual request object. For the EntityManager, you can inject `javax.inject.Provider`. When you need it, call `get()` to get the `EntityManager` – Paul Samsotha Oct 14 '15 at 12:59
  • so my EMFactory has to become a Provider... i will try.. thanks! – Alex Oct 14 '15 at 13:02
  • Just a question... should have i to register the provider in requestscope too? – Alex Oct 14 '15 at 13:06
  • @Alex It has nothing to do with a provider in the sense you are thinking. You don't need to register anything. Just inject `javax.inject.Provider` the exact same way that you would inject just the `EntityManager`. Only difference is that you need to call `get` on the `Provider` to lazily retreive the `EntityManager`. – Paul Samsotha Oct 14 '15 at 13:09
  • Oh... i got it... thnx again – Alex Oct 14 '15 at 13:12
  • Is there the possibility to use the InstantiationService.getInstatiationData like with the normal factory? I need to get the caller – Alex Oct 15 '15 at 17:41
  • What about open transaction and rollback? – Avihai Marchiano Nov 20 '16 at 14:09
  • See also http://stackoverflow.com/a/17133081/873282 for an example how to integrate that in an Application. – koppor Feb 19 '17 at 10:04