Why do we have to manually flush() the EntityManager in a extended PersistenceContext?
In our J2EE application, we use a EJB-3 stateful bean to allow the front code to create, modify and save persistent entities (managed through JPA-2).
It looks something开发者_如何转开发 like this:
@LocalBean
@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class MyEntityController implements Serializable
{
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager em;
private MyEntity current;
public void create()
{
this.current = new MyEntity();
em.persist(this.current);
}
public void load(Long id)
{
this.current = em.find(MyEntity.class, id);
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void save()
{
em.flush();
}
}
Very important, to avoid too early commits, only the save()
method is within a transaction, so if we call create()
, we insert nothing in the database.
Curiously, in the save()
method, we have to call em.flush()
in order to really hit the database. In fact, I tried and found that we can also call em.isOpen()
or em.getFlushMode()
, well anything that is "em-related".
I don't understand this point. As save()
is in a transaction, I thought that at the end of the method, the transaction will be committed, and so the persistent entity manager automatically flushed. Why do I have to manually flush it?
Thanks, Xavier
To be direct and to the metal, there will be no javax.transaction.Synchronization
objects registered for the EntityManager in question until you actually use it in a transaction.
We in app-server-land will create one of these objects to do the flush()
and register it with the javax.transaction.TransactionSynchronizationRegistry
or javax.transaction.Transaction
. This can't be done unless there is an active transaction.
That's the long and short of it.
Yes, an app server could very well keep a list of resources it gave the stateful bean and auto-enroll them in every transaction that stateful bean might start or participate in. The downside of that is you completely lose the ability to decide which things go in which transactions. Maybe you have a 2 or 3 different transactions to run on different persistence units and are aggregating the work up in your Extended persistence context for a very specific transaction. It's really a design issue and the app server should leave such decisions to the app itself.
You use it in a transaction and we'll enroll it in the transaction. That's the basic contract.
Side note, depending on how the underlying EntityManager is handled, any persistent call to the EntityManager may be enough to cause a complete flush at the end of the transaction. Certainly, flush()
is the most direct and clear but a persist()
or even a find()
might do it.
If you use extended persistence context all operations on managed entities done inside non-transactional methods are queued to be written to the database. Once you call flush() on entity manager within a transaction context all queued changes are written to the database. So in other words, the fact that you have a transactional method doesn't commit the changes itself when method exits (as in CMT), but flushing entity manager actually does. You can find full explanation of this process here
Because there is no way to know "when" the client is done with the session (extended scope).
精彩评论