I use EntityManager to save/update my entities in database and Hibernate as jpa provider. My transactions are handled by container.
The problem: I need to add an entity that might have been already stored in database, so an exception will be thrown. In this case I need to repeat insertion but with another value. But as long as an exception is thrown the session has gone bad and I need to create a new session and rollback the transaction. How can I do this when I'm using CMT? Or if there is another way to do this?
Thank you.
You could use the TransactionAttribute(REQUIRES_NEW) for your persistence method. If the bean invoking your method will catch an exception, it might do some changes and invoke the method once again.
This will rollback just the persistence-method transaction - not the invoking bean one.
However, remember that if your Use Case doesn't require you to do EntityManager#persistence(-), you might be interested in EntityManager#merge(-) operation. It will persist the entity if it doesn't already exist or update it if it already exists (the existence is checked based on the PK).
HTH.
You might want to use EntityManager#find(Class, PK) to check for an already persisted entity. A null result means there's no such entity ( ----> persist(.) ), otherwise you update with the merge(.) method.
Related
I am trying to implement transaction management in EJB application with Openjpa as ORM tool.
I am trying to implement a scenario where if a db update operation fails for an entity, then db operations for all other entities in same transaction
also rollback. So I have an entity A and I explicitly set one of it's non-nullable field to null and call entitymanager.merge() method for this entity.I expect that due to non-nullable constraint db operation would fail and PersistenceException would be thrown. But nothing of that sort happens, though A's state is also not persisted.
But if I first call find() using A's id and then call merge(), I get exception.
I used to think that merge() alone is sufficient to take care of all. Why do I need to explicitly call find() before merge?
Changes are not required to be synchronized to the database until you call em.flush(), or commit the associated transaction.
I have a trigger in DB that forbids inserting duplicated data. When I enter duplicated data, it adds nothing to the table, OptimisticConcurrencyException is thrown and I am swallowing (ignoring) this exception. When I try to enter new correct object, EF tries to run both INSERTs and it fails again on the first one.
How can I recover from this, all examples are discussing failed UPDATES, is there anything about INSERT? As I have read creating new DatabaseContext will solve the problem, but I cannot do it that way.
Creating a new DatabaseContext is always the best choice (since Hibernate).
In your case you need to remove the entity that caused the error from the context.
((IObjectContextAdapter)context).ObjectContext.Detach(entity);
You can ask ObjectContext to ignore all the changes after the ignorable exception is thrown. This way the added entity is marked Unchanged; hence in the next SaveChanges call, context won't consider it to manipulate DB:
(yourContextObject as IObjectContextAdapter).AcceptAllChanges();
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.
In my EJB project deployed on WAS 6.1, when I try to call EntityManamger.merge(obj), I get this exception:
Exception data: <openjpa-1.0.4-SNAPSHOT-r420667:955062 nonfatal store error> org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object "com.data.entity.SomeEntity#23aa23aa". If this is a new instance, make sure any versino and/or auto-generated primary key fields are null/default when persisting.
FailedObject: com.bmo.ctp.data.entity.attribute.RoleResServiceAttribute#23aa23aa
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2368)
at org.apache.openjpa.kernel.AttachStrategy.persist(AttachStrategy.java:94)
at org.apache.openjpa.kernel.VersionAttachStrategy.attach(VersionAttachStrategy.java:95)
at org.apache.openjpa.kernel.AttachManager.attach(AttachManager.java:241)
at org.apache.openjpa.kernel.AttachStrategy.attachCollection(AttachStrategy.java:333)
at org.apache.openjpa.kernel.AttachStrategy.replaceCollection(AttachStrategy.java:301)
at org.apache.openjpa.kernel.AttachStrategy.attachField(AttachStrategy.java:220)
at org.apache.openjpa.kernel.VersionAttachStrategy.attach(VersionAttachStrategy.java:157)
at org.apache.openjpa.kernel.AttachManager.attach(AttachManager.java:241)
at org.apache.openjpa.kernel.AttachManager.attach(AttachManager.java:101)
at org.apache.openjpa.kernel.BrokerImpl.attach(BrokerImpl.java:3161)
at org.apache.openjpa.kernel.DelegatingBroker.attach(DelegatingBroker.java:1142)
at org.apache.openjpa.persistence.EntityManagerImpl.merge(EntityManagerImpl.java:665)
at com.ibm.ws.jpa.management.JPAExEmInvocation.merge(JPAExEmInvocation.java:335)
at com.ibm.ws.jpa.management.JPAEntityManager.merge(JPAEntityManager.java:123)
I do not have the source for JPAEntityManager.java; but I thought the EntityExistsException cannot be thrown by the merge() operation since merge will first do a check to see if entity exists. If not, insert it. If yes, update it.
What is going wrong here?
Please check how your entitymanager checks the existence of an entity. Is it using Cache? or is it using a DB Query. This issue generally occurs, if the existence check is done through cache.
if you are using eclipselink JPA, this configuration is maintained in orm.xml
Thanks,
Anand
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.