jakarta-eeejbinterceptorentitymanager

Automatically close EntityManager in an interceptor


I have a ejb + REST application using jakarta ee 10, deployed in a Wildfly 29 server; the application uses multitenancy. When a user logs in, i have to check which database the user is going to use to create an EntityManager for that user in each transaction

In my Business layer i have some EJB annotated as @Stateless, and every one of them extends a single class called BaseService which i use to handle common operations; one such operation is the provisioning of the EntityManager.

I have read that the proper way of handling the EntityManager is to instantiate one for every method in the business layer and close it once the method is finished. In order to do that, my BaseService class looks something like this

public class BaseService extends BaseBean {

@Resource
private SessionContext context;

@PersistenceUnit
private EntityManagerFactory entityManagerFactory;

protected EntityManager entityManager;

public void openEntityManager() {
    if (entityManager == null) {
        String clientIdentifier = getClientIdentifierForCurrentUser();
        final MultiTenantResolver tenantResolver = (MultiTenantResolver) ((SessionFactoryImplementor) entityManagerFactory).getCurrentTenantIdentifierResolver();
        tenantResolver.setTenantIdentifier(clientIdentifier);
        entityManager = entityManagerFactory.createEntityManager();
    }
}

public void closeEntityManager() {
    if (entityManager != null && entityManager.isOpen()) {
        entityManager.close();
    }
}

}

In the @Stateless beans i am using an interceptor that looks like this

public class ServiceInterceptor {

@AroundInvoke
public Object handleEntityManager(InvocationContext ctx) throws Exception {
    try {
        BaseService service = (BaseService) ctx.getTarget();
        service.openEntityManager();
        return ctx.proceed();
    } catch (Exception e) {
        throw e;
    }
}

}

This is working so far; when i execute any method in my @Stateless beans, the entityManager property is ready to use, but the problem is, how do i close it automatically ?

I could call closeEntityManager as the last line of every method but that would be too error prone, so i would prefer to avoid it

Maybe is there a better way to handle the entityManager "injection" ?

Any advice is appreciated


Solution

  • You could use CDI to inject with something like this:

    public class BaseService extends BaseBean {
    
        @Resource
        private SessionContext context;
    
        @PersistenceUnit
        private EntityManagerFactory entityManagerFactory;
    
        @Produces
        public void openEntityManager() {
            String clientIdentifier = getClientIdentifierForCurrentUser();
            final MultiTenantResolver tenantResolver = (MultiTenantResolver) ((SessionFactoryImplementor) entityManagerFactory).getCurrentTenantIdentifierResolver();
            tenantResolver.setTenantIdentifier(clientIdentifier);
            entityManager = entityManagerFactory.createEntityManager();
        }
    
        public void closeEntityManager(@Disposes final EntityManager entityManager) {
            entityManager.close();
    }
    

    For you usage you'd simply do @Inject EntityManager entityManager.