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
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
.