EntityManager merge throwing EntityExists exception - openjpa

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

Related

JPA merge not working as expected

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.

EntityFramework Insert fails due to Trigger and OptimisticConcurrencyException is thrown, how to handle it?

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();

jpa concurrency CMT exception handling

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.

EntityManager trying to insert entities without being prompted

Perhaps I'm over-simplifying this by only giving you a small snippet of code (and I'll post more if that is the case) but I figure, initially, less is better:
I have an entity of type Asset which has a field of type Location, which is also an entity. When I set the Location of an Asset I must also set the Location of its children.
Location location = asset.getLocation();
em.merge(location);
em.flush();
childAsset.setLocation(asset.getLocation());
em.flush();
When I run flush(), I get the following exception:
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SWRADMIN.LOCATION_PK) violated
My question is...why is this Location object even trying to be persisted? All I'm doing is setting a variable in an entity.
This was working fine before, but we just switched to using Eclipselink and Java EE 6 and this problem popped up.
Solution?: I used the idea of "detaching" from below and made the following change:
Location location = asset.getLocation();
em.detach(childAsset);
childAsset.setLocation(asset.getLocation());
em.merge();
em.flush();
and it worked! I'm confused as to why, though...you would think the auto-syncing would do the same thing.
If the object is in managed state, then entity manager will synchronize it with the underlying database by persisting the object implicitly (probably at the end of the transaction) or explicitly when you call em.flush() method.
You can use em.detach(entity) to detach a single entity or em.clear() to detach all the entities. Then the changes being made on an entity/entities will not get reflected in the database.
To better handle this, you can use BMT (Bean Managed Transaction), where you have to handle the entity persistence, transaction manually.
Edit :
Location location = asset.getLocation();
childAsset.setLocation(location);
em.merge(childAsset);
em.flush();
So, the location is an existing location, or a new object?
How was it read, was it read from another transaction or entity manager or detached in some way? If so, then you need to re-read (find) or merge it.
If it is new and related to a managed object, then yes, flush must write it.
From the code, it looks like you are using the unmanaged version of location and associating that to the ChildAsset. If your childAsset->Location relation is marked cascade persist, then the spec requires perist will be called on Location on flush or commit. Since Location is not a managed object, it requires an exception on persist.
When Location is managed (such as when you call merge on ChildAsset or if you had used the managed instance of Location returned from the
em.merge(location); call), the persist operation on ChildAsset->Location is a no-op.
Do not associate unmanaged entities to relationships marked cascade persist.

Removing an entity, but using the same primary key to add a similar entity after the removal

Im trying to remove an entity which has a unique PK like : 80253
I remove this entity by doing the follow lines of code:
myEntityType1 = getEntityManager().find(MyEntityType1.class, 80253);
getEntityManager().remove(myEntityType1);
getEntityManager().flush();
These bits of code actually deletes the rows from my database and all its cascading objects properly, and Im very happy about this. Now the problem occurs when I now need to create a similar entity that uses the same primary key (which should now be gone right?).
MyEntityType2 myEntityType2 = new MyEntityType2 ();
myEntityType2.copyData(myEntityType1); //access the data from the other object
//and retrieves the id 80253. myEntityType2 now has 80253 as ID.
getEntitymanager().persist(myEntityType2);
Now this is where I get a unique constraint SQL error. Im trying to insert a ID which already exists and the changes are automatically rolled back (the old entity is no longer deleted). This happens after I see in my logger that toplink has deleted the records of the old entity.
Does anyone know how this happens, and why its not working? For the record Ive tried merging, closing, clearing of the entityManager, but nothing seems to work.
It seems to me that JPA might do some bad caching or something. I hope someone has a good answer for me! =)
Update: Theres no longer an issue with unique ID constraints, but I create a new subclass with the same primary key which has been deleted I get the following exception:
Exception Description: Trying to invoke [setApprovedWhen] on the object [null]. The
number of actual and formal parameters differs, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: object is not an instance of
declaring class
It seems to me that it wont let me change the object into a different subclass?
EDIT:
Try with explicitly start and commit transaction.
Deleting an entity is as simple as
calling EntityManager method
remove(Object entity) as the following
example shows. The entity you delete
must be managed: that is, it must have
been previously read in the current
persistence context.
entityManager.getTransaction().begin();
myEntityType1 = getEntityManager().find(MyEntityType1.class, 80253);
getEntityManager().remove(myEntityType1);
entityManager.getTransaction().commit();
When the transaction is completed, or
you call EntityManager method flush(),
the entity will be deleted.
In a container managed persistence
context the transaction boundaries
will be controlled by the container.