I found following information from the spec. But it's not clear enough for me who is not an english native.
The PostPersist and PostRemove callback methods are invoked for an entity after the entity has been made persistent or removed. These callbacks will also be invoked on all entities to which these operations are cascaded. The PostPersist and PostRemove methods will be invoked after the database insert and delete operations respectively. These database operations may occur directly after the persist, merge, or remove operations have been invoked or they may occur directly after a flush operation has occurred (which may be at the end of the transaction). Generated primary key values are available in the PostPersist method.
My question is any transaction related jobs can be rolled back after #PostRemove?
Let's say my entity deletes some offline files on #PostRemove
class MyEntity {
#PostRemove
private void onPostRemove() {
// delete offline files related to this entity
// not restorable!
}
}
Is it possible that those offline files deleted from the storage and the entity still left in the database? (by rollback?)
Yes, it is possible that your files are deleted and your entites are still left in db after a rollback. #PostRemove is in transaction.
If you want to be absolutely sure that your files are deleted if and only if the transaction is successfully completed then you should delete the files after the commit() succeeds not using the callback methods. But if you also need to be sure that the entity is removed if and only if the file is deleted, then you have a problem. You need a transactional way of accessing the file system.
For a simple solution move your files into a to_be_deleted-folder during the db-transaction. Therefore you can use the callback methods. The files are finally deleted when commit() succeeds and restored on failure.
If you want to elaborate it a bit more and your application is running in a java EE container, then you might want to look at CDI events or even at a jca adapter. If you are using Spring you can register a TransactionSynchronizationAdapter see this answer.
It depends.
If you're using multiple flushes (EntityManager#flush()), the transaction could still be rolled back. Otherwise, any callbacks prefixed with Post are executed after the database transaction is complete.
Related
I'm using EclipseLink (JPA). I've got a service that renames some files and calls a DAO which updates database tables accordingly. This happens in a transaction, and there might be a rollback. So that the DB stays in sync with the file system, I want to at least attempt to revert the file rename, and log an error when that can't be done. Is there some way to register a Runnable (or similar) with the transaction so that it executes on rollback?
EclipseLink has SessionEventListener which can respond to pre (preRollbackTransaction) and post rollback events, and I can get the transaction from the EntityManager. So I was thinking I could create some kind of global registry. However, I can't figure out how to match up the SessionEvent available in the listener with the EntityTransaction available from EntityManager.getTransaction(). Is there some way to do this, or is there a more straightforward way to provide a callback on rollback?
I could simply use a try-catch and execute the revert code in the catch, but it is possible the rollback will occur after the session method is returned (i.e. higher up the stack).
I'm using JPA EclipseLink 2.0 with Glassfish 3.1.2.2
I want to know if after I call
em.flush()
em.clear()
The objects are immediatly commited to the database. My problem is I'm doing so many transactions that I'm getting OutOfMemory. I want to avoid this by flushing the transaction's objects.
After I flush and clear, I can't see any immediate entity commited to the database, I can only see them AFTER the whole process is done, which tells me this isn't actually commiting.
If flush and clear doesn't commit:
1) What does it actually do?
2) Why am I no longer getting OutOfMemory?
Please tell me if I'm right:
The objects that were allocated in my RAM are sent to the database, but changes are not yet commited. This only means I cleared my RAM, the objects are now in the DB server but the transaction is not yet commited.
Entities are synchronized to the connected database at transaction commit time. If you only have n = 1 ongoing transaction (here: JTA/container managed), changes on one or more entities get written to the DB the moment you call flush() on the EntityManager instance.
However, changes become "visible" only after the transaction has been properly executed by the container (here: Glassfish) which is responsible for transaction handling. For reference, see. section 7.6.1 (p. 294) of JPA Spec 2.0 which defines:
A new persistence context begins when the container-managed entity manager is invoked (Specifically, when one of the methods of the EntityManager interface is invoked) in the scope of an active JTA transaction, and there is no current persistence context already associated with the JTA transaction. The persistence context is created and then associated with the JTA transaction.
The persistence context ends when the associated JTA transaction commits or rolls back, and all entities that were managed by the EntityManager become detached.
In section 3.2.4 (Synchronization to the Database) of the JPA Spec 2.0 we find:
The state of persistent entities is synchronized to the database at transaction commit.
[..]
The persistence provider runtime is permitted to perform synchronization to the database at other times as well when a transaction is active. The flush method can be used by the application to force synchronization.
It applies to entities associated with the persistence context. The EntityManager and Query setFlushMode methods can be used to control synchronization semantics. The effect of FlushModeType.AUTO is defined in section 3.8.7. If FlushModeType.COMMIT is specified, flushing will occur at transaction commit; the persistence provider is permitted, but not required, to perform to flush at other times. If there is no transaction active, the persistence provider must not flush to the database.
Most likely in your scenario, the container (Glassfish) and/or your application is configured for FlushModeType.COMMIT(*1). In case FlushModeType.AUTO is in place, it is up to the Persistence Provider (EclipseLink) which "is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query." (Section 3.8.7, p. 122)
By contrast, the clear() method does NOT commit anything by itself. It simply detaches all managed entities from the current persistence context, thus causing any changes on entities which have not been flushed (committed) to get lost. For reference, see p. 70 of the linked JPA Spec.
With respect to the OutOfMemoryError, it's hard to tell what's causing this under which circumstances, as you did not provide much detail either. However, I would:
read the aforementioned sections of the JPA specification
check how your environment is configured and
reevaluate how your application is written/implemented, potentially making false assumptions on the transaction handling of the container it is running in.
Related to 2., you might check your persistence.xml whether it configures
<property name="eclipselink.persistence-context.flush-mode" value="COMMIT" />
and change it to AUTO to see if there is any difference.
Hope it helps.
Footnotes
*1: But that's a good guess, as you did not provide that much detail on your setup/environment.
On JPA transaction commit, JPA is doing flush automatically. You should see object in DB right after first transaction end, not only after whole process end. Check if really do more transactions or just one.
I'm experiencing a very strange behaviour with Transactions using play-2.2.2 with JPA and Eclipse-Link.
My Controller-Action is annotated with #Transactional like this:
#Transactional
public static Result submitOrder() {
// class does call private Methods which persist some entities (methods not annotated)
//...
Action is calling private methods to persist data (This should happen in the same transaction since no other Transaction is started).
During the Methods calls (at random locations) data gets written to db (inserts and updates). Debuging shows that the same (active) transaction is used before and after the write. EntityTransactionImpl:commit is never executed and transaction stays active until request is finished ( watched play.db.jpa.JPA.em().getTransaction() )
How is it possible that the data is written although transaction is still active?
It breakes the setRollbackOnly Mechanism since already written data isn't rolled back.
May there be any kind of Timeout that issue these writes.
Can you suggest any debug-entry-point to narrow down the problem (where can i start debuging the actual write-operations, if not in EntityTransactionImpl:commit)?
Dependencies in build.sbt
persistence.xml
The above described behaviour seemed very odd at first, but then i read about FlushMode and now it makes sense.
The FlushMode of eclipselink as well as hibernate is set to FlushModeType.AUTO
FlushModeType.AUTO automatically flushes Entities to the DB when it thinks it's neccessary. This can be because of an readable operation (Query) on a Persited (but not flushed) Entity but it also happened somehow randomly during my observations.
This breaks the rollback-on-failure mechanism, which I thought must be the standard behaviour of #Transactional.
To achive a propper rollback (on failure or if setRollbackOnly() is set), of all persisted but not flushed entities on transcaction commit, you have to explicitly set the FlushMode at the beginning of your Action.
JPA.em().setFlushMode(FlushModeType.COMMIT);
If you're using Eclipselink, you can also set the following property to make it default behaviour:
<property name="eclipselink.persistence-context.flush-mode" value="commit" />
Links which helped me understand:
Eclipselink Context Flushmode
what to use flush mode auto or commit
performance tuning hibernate
public void whyEntityExistsExceptionisnotthrown(){
EntityManager em=getEntityManager();
try{
Partner partnerOne=em.find(Partner.class, 1L); // from the database
System.out.println("Partner partnerOne information-----------> "+partnerOne.getName());
Partner partnerTwo =new Partner();
partnerTwo.setIdpartner(1L);
partnerTwo.setName("Partner 200");
em.persist(partnerTwo);
Partner partnerThree=em.find(Partner.class, 1L);
// the method find has two entities with the id 1L. I think this could be a problem.
if(em.contains(partnerOne))
System.out.println("PartnerOne managed");
if(em.contains(partnerTwo))
System.out.println("PartnerTwo managed");
System.out.println("Partner partnerTwo information-----------> "+partnerTwo.getName());
System.out.println("Partner partnerThree information-----------> "+partnerThree.getName());
}catch(EntityExistsException e){
System.out.println("The entity already exist");
}
}
Through this post I try to see that problems can arise by allowing two entities have the same id in a persistence context.
The question is:
Is there a way to avoid that there may be two managed entities with the same id in a persistence context before calling the flush method or commit?
Why not throw the exception persist?
If I call this method the result is:
First call to whyEntityExistsExceptionisnotthrown
partnerOne: the information from the database (I've got a partner in database with id=1)
partnerTwo: Name=Partner 200
partnerThree: Name=Partner 200 (but could have been the information of partnerOne.
Next Call
partnerOne ------> Name=Partner 200
partnerTwo ------> Name=Partner 200
partnerThree ----> Name=Partner 200
According to the documentation of the persist() method, it throws an
EntityExistsException - if the entity already exists. (If the entity
already exists, the EntityExistsException may be thrown when the
persist operation is invoked, or the EntityExistsException or another
PersistenceException may be thrown at flush or commit time.)
So to be sure that your persist() reaches the DB, you should call em.flush() right after persisting the entity and you will get one exception (either EntityExistsException on calling persist or EntityExistsException/PersistenceException flush). Yes, one could ask himself why the JPA specification is not very clear in this point, but I am pretty sure there is good reason for that, like performance (trying to make a single I/O operation to DB). So, if you want portable/working code, call the flush() operation. You probably do not understand that most operations like remove, merge, persist are not guaranteed to reach to DB until the transaction commit. So it is your duty to call flush() when needed.
So to answer your questions:
Is there a way to avoid that there may be two managed entities with
the same id in a persistence context before calling the flush method
or commit?
I doubt that, and also I haven't found so far the need for that (!call flush!). Also as far as I know, the JPA spec. does not require that.
Why not throw the exception persist?
Because the persist is not guaranteed to be synchronized to DB in that moment. The documentation says that pretty clear.
Some important notes:
As I understand from your code, your ID field is NOT autogenerated. Usually the applications lets the DB to generate the ID, so that calling persist gets synchronized to DB.
Also, in your code (without testing it) I am sure that partnerOne==partnerThree is evaluated as true, meaning they are actually EXACTLY THE SAME object. The only problem is with the partnerTwo, which you must flush.
I have an EJB, whose method (among other things) persists JPA entity. If the method throws an error, the transaction is rolled back and the entity is not persisted.
However, I do want that entity to be persisted regardless of any exceptions, that might occur in the EJB method.
I'm using WebSphere 7.0, EJB3.0, JPA 1.0 (OpenJPA that is in WAS), DB2, if it matters.
I tried setting #TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) on top of EJB; with that, entity is not persisted even if there is no exception. I also tried commiting transaction myself (em.getTransaction().commit()), but getTransaction() throws exception (because transactions are managed by container).
Use bean-managed transactions.
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class MyEJB {
#PersistenceContext(unitName="...")
private EntityManager _em;
#Resource
private UserTransaction _utx;
public void myEJBMethod() {
_utx.begin();
// Use _em
_utx.commit();
// Do other work that might throw an exception.
}
}
Alternatively, use TransactionAttributeType.REQUIRES_NEW as suggested by edalorzo.
I am not an expert on EJBs, but I have been dealing with JPA and transactions for a few days now.
I recently answered another question about how entities resided in a context, and how this works in Java EE applications, the context is linked with your JTA transaction.
You can see details of this answer by clicking here. I think it is useful to understand how to context works in order to comprehend the nature of problems like the one you describe.
If you do not provide transaction support, then there is nothing to persist from the container standpoint, and therefore, your changes to the context are transient.
Also you have to consider that once an exception occurs, your context becomes invalid, and the entities in it get detached. (There are a few exceptions to this, like NoResultException).
Thus, from that point on, if you want to commit something, you need a new JTA transaction, with a new fresh JPA context in order to be able to commit changes to the database.
As I said, I am not an expert in EJBs, but if your method fails due to exceptions and you still would like to retry the transaction again by re-invoking the method, then you could force a new transaction to be created every time the method is invoked and by this, you would create a new fresh JPA context.
On the other hand, if you want your modifications to the entities to be persisted, regardless of exceptions in the method, then you might like to consider moving the code that is updating the entities to a new EJB method defined to start a new transaction (TransactionAttributeType.REQUIRES_NEW) every time you invoke it.
By the time this second inner method finishes, your work over the transactions will be automatically flushed to the database, regardless of the outer method of you EJB failing.
Basically, you would be providing a new context for your entities, and linking such context to a new transaction, scoped to commit when the inner method completes.
The natural behavior in EJB containers, as far as I understand, is that ever method joins the already existing transaction, and this is what you might like to prevent, from my point of view.
Another alternative: if you want to control your context using a different transaction support then you might like to consider providing a resource-local based persistence unit and you can manually instantiate your entity manager and control transaction scope as you wish. But honestly, this does not sound like a good idea to me, at least no in the context of the problem that you described.