Frameworks like MyFaces Orchestra provide a feature which allows keeping
an EntityManager
across multiple requests. That means it is not
required to call EntityManager#merge
to add detached entities to the
context. However, several application architectures do not allow such an
approach (due to different reasons like scalability). In theory that
sounds nice and it works pretty well for small to medium sized projects
especially if an application does not rely on session replication in clusters.
That also means that such an approach restricts your target environment
from the very beginning. One of the base problems is that an
EntityManager
is not serializable. Beans which are scoped in a
normal-scoped CDI context have to be serializable. So by default it
is not allowed by CDI to provide a producer-method which exposes, for example, a
conversation scoped EntityManager
as it is. We do not recommend this approach and therefore it is not available out-of-the-box.
However, if you really need this approach to avoid calling #merge
for
your detached entities, it is pretty simple to add this functionality.
Usage of a Simple extended EntityManager
@Inject
private EntityManager entityManager;
As you see the usage is the same. You do not have to use
ExtendedEntityManager
at the injection point. It is just needed in the
producer-method:
Producer for an extended EntityManager (non-EE server)
public class ExtendedEntityManagerProducer
{
@PersistenceContext
private EntityManager entityManager;
@Produces
@RequestScoped
protected ExtendedEntityManager createEntityManager()
{
return new ExtendedEntityManager(this.entityManager);
}
protected void closeEntityManager(@Disposes ExtendedEntityManager entityManager)
{
if (entityManager.isOpen())
{
entityManager.close();
}
}
}
Producer for an extended EntityManager (EE server)
@ApplicationScoped
public class ExtendedEntityManagerProducer
{
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
@Produces
@Default
@RequestScoped
public ExtendedEntityManager create()
{
return new ExtendedEntityManager(this.entityManagerFactory.createEntityManager());
}
public void dispose(@Disposes @Default ExtendedEntityManager entityManager)
{
if (entityManager.isOpen())
{
entityManager.close();
}
}
}
Implementation of a simple extended EntityManager
@Typed()
public class ExtendedEntityManager implements EntityManager, Serializable
{
private static final long serialVersionUID = 3770954229283539616L;
private transient EntityManager wrapped;
protected ExtendedEntityManager()
{
}
public ExtendedEntityManager(EntityManager wrapped)
{
this.wrapped = wrapped;
}
}
This approach just works if it does not come to serialization of this
wrapper, for example in case of session-replication. If those beans get
serialized, you have to overcome this restriction by storing the
persistence-unit-name and recreate the EntityManager
via
Persistence.createEntityManagerFactory(this.persistenceUnitName).createEntityManager();
and sync it with the database before closing it on serialization.
Furthermore, you have to intercept some methods of the EntityManager
to merge detached entities automatically if those entities get
serialized as well. However, as mentioned before we do not recommend
such an approach.