Multiple representations of the same entity are being merged, Detached - jpa

I have been getting an error while trying to update a list of entities containing persisted entity and detached entity both (newly created entity) into my db using jpa2.0.
My entity contains internal entities which are giving an error (mentioned in the title) when merging the data:
Class superclass{
private A a;
private string name;
//getter setters here...
}
Class A{
private long id;
#onetoone(cascade=CascadeType.All, fetch=FetchType.Eager)
private B b;
#onetoone(cascade=CascadeType.All, fetch=FetchType.Eager)
private C c;
//getter setters here...
}
Class Dao{
daoInsert(superclass x){
em.merge(x);
}
}
I want any entity sent for persisting to be merged into the db.
Hibernate does provide solution for this by adding the following to the persistence.xml
Is there something I can do in jpa same as hibernate.
Please do not suggest to find the entity using em.find() and then update manually because I need both entities the persisted entity and the newly created entity too.
Also I'm using spring form to persist the entire patent entity into db.
I am sorry if I'm not clear enough, this is my first question and I'm really a beginner.
Any help will be most appreciated.

Found an answer to the question myself today.You just need to
remove CascadeType.MERGE from the entity that is not allowing you to persist the detached entity.
if you're using CascadeType.ALL then mention all cascade type other than CascadeType.MERGE.

Now removing CascadeType.MERGE from cascade is one solution but not a best solution because after removing MERGE from Cascade you won't be able to update the mapped object ever.
If you want to merge the Detached entity with Hibernate then clear the entity manager before you merge the entity
entityManager.clear();
//perform modification on object
entityManager.merge(object);

To solve this problem make sure to specify that the identifiers of your objects are automatically generated by adding #GeneratedValue(strategy = GenerationType.IDENTITY) on the identifaint such as id.
In this way when the merge will be carried out, the identifier of the elements to merge will be automatically incremented, compared to the other object already recorded in the database to avoid primary key conflicts

Related

Hibernate 5 + JPA 2 doesn't cascade delete over join table

We have this relationship:
public class RuleProviderEntity implements Serializable
{
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#OrderColumn(name = RuleEntity.RULE_SEQUENCE)
private List<RuleEntity> rules;
}
This alone creates a join table with 2 keys and the RULE_SEQUENCE column. So far good and works for SELECTs.
Now there's a JQL query
DELETE FROM RuleProviderEntity WHERE ...
But this fails to cascade deleting the RuleEntity rows. It just deletes the RuleProviderEntity and leaves the RuleEntity intact.
Is this supposed to work in JPA 2 and it's a Hibernate bug, or am I missing something in the config?
I know I could add #JoinTable but it would only override the defaults.
Also orphanRemoval seems not necessary here.
Maybe I could do a workaround with #PreRemove but not sure how.
You mean a JPQL Bulk Delete query is issued? rather than em.remove().
A Bulk Delete query will NEVER respect cascade semantics and is not intended to (nor will it keep managed objects in-memory consistent with the datastore). If you want cascading then you need to call em.remove(). If in doubt about this look at the JPA spec.

JPA error "Cannot merge an entity that has been removed" trying to delete and reinsert a row with SpringData

I've an Entity (with a primary key that is not generated by a sequence) like this in a Spring Data JPA/Eclipselink environment :
#Entity
#Table(name="MY_ENTITY")
public class MyEntity implements Serializable {
#Id
#Column(insertable=true, updatable=true, nullable=false)
private String propertyid;
\\other columns
}
and I'm trying to delete a row from the table and reinsert it (with the same primary key).
My approach is to call deleteAll() to clean the table and then save() the new Entity :
#Transactional
public void deleteAndSave(MyEntity entity) {
propertyInfoRepository.deleteAll();
propertyInfoRepository.flush(); // <- having it or not, nothing changes
propertyInfoRepository.save(entity);
}
but this gives me this error :
Caused by: java.lang.IllegalArgumentException: Cannot merge an entity that has been removed: com.xxx.MyEntity#1f28c51
at org.eclipse.persistence.internal.sessions.MergeManager.registerObjectForMergeCloneIntoWorkingCopy(MergeManager.java:912)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:494)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:271)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:3495)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.mergeCloneWithReferences(RepeatableWriteUnitOfWork.java:378)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:3455)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.mergeInternal(EntityManagerImpl.java:486)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.merge(EntityManagerImpl.java:463)
....
What am I doing wrong?
I do not understand why it is trying to merge the entity instead of simply reinsert it after its deletion.
Thanks for your help!
Directly to answer your question:
The problem is that the entity that you try to save has already a persistent identity, i.e an ID, which is why your repository will try to merge, and not to persist the entity.
If you see this question it seems that it is triggered (at least) on the level of the Spring Repository, so you might consider overriding the save method of the repository and test whether the problem is still there.
JPA EntityManager keeps track of the state of each managed entity. In your case, you delete the entity and then try to merge it, which raises the exception. I can't tell if your approach is correct (seems weird to delete and then merge) since you don't provide the whole picture but you can try the following:
Assuming em is your EntityManager and entity your entity:
em.remove(entity); //This will perform the delete
MyEntity detachedEntity = em.detach(entity); //Gets a detached copy of the entity, EM will not operated on this unless told to do so (see below)
detachedEntity.setId(null) // Avoid duplicate key violations; Optional since you are deleting the original entity
em.persist(detachedEntity); // This will perform the required insert

JPA: Cascading on shared objects

Say i have class called Record, with a many-to-one relationship to a class called Artist.
If i define a cascade option as such:
class Record{
#ManyToOne(cascade = CascadeType.ALL)
private Artist artist;
...
}
Say i have a number of records managed by my EntityManager and some of these share the same Artist.
What happens when i call, update, merge, detach, remove and so on on the Record objects?
Will the artist be removed for example? Will it be detached? If so, what happens to the other Record classes that references that Artist?
Since you configured the association with cascade = CascadeType.ALL, all the operations done on a record will also be done on the associated Artist. Removing the record will thus remove the artist. This will fail with a foreign key constraint exception (if they're correctly configured in the database) if another record also references the same artist.
Configuring such a cascade on a ManyToXxx associations doesn't make much sense.

JPA #EmbeddedId: How to update part of a composite primary key?

I have a many-to-many relationship where the link table has an additional property. Hence the link table is represented by an entity class too and called Composition. The primary key of Composition is an #Embeddable linking to the according entities, eg. 2 #ManyToOne references.
It can happen that a user makes an error when selecting either of the 2 references and hence the composite primary key must be updated. However due to how JPA (hibernate) works this will of course always create a new row (insert) instead of an update and the old Composition will still exist. The end result being that a new row was added instead of one being updated.
Option 1:
The old Composition could just be deleted before the new one is inserted but that would require that the according method handling this requires both the old and new version. plus since the updated version is actually a new entity optimistic locking will not work and hence last update will always win.
Option 2:
Native query. The query also increments version column and includes version in WHERE clause. Throw OptimisticLockException if update count is 0 (concurrent modification or deletion)
What is the better choice? What is the "common approach" to this issue?
Why not just change the primary key of Composition to be a UID which is auto-generated? Then the users could change the two references to the entities being joined without having to delete/re-create the Composition entity. Optimistic locking would then be maintained.
EDIT: For example:
#Entity
#Table(name = "COMPOSITION")
public class Composition {
#Id
#Column(name = "ID")
private Long id; // Auto-generate using preferred method
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn( .... as appropriate .... )
private FirstEntity firstEntity;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn( .... as appropriate .... )
private SecondEntity secondEntity;
....

Controlling DDL generation in Toplink Essentials

I am having some trouble with the DDL generation of Toplink Essentials. I am developing a Glassfish 2.1 based application and use JPA for persistence.
I have an object graph where a parent entity of class A owns a set of entities of class B. Entites B come in several flavors which is modelled using inheritance. One such flavor is a composite entity class BC that bundles a set several other B entites. All entites B in a BC must also be owned by the same entity A as B. Note that not all entites B of an entity A have to be part of a composite BC, they can also be standalone.
So basically that maps to the following classes:
#Entity
class A {
#ManyToOne(mappedBy="owner", cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
Set<B> bs;
}
#Entity
#Inheritance
abstract class B {
#Id
long id;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
A owner;
#ManyToOne(optional = true)
BC composite;
}
#Entity
class BC extends B {
#OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, mappedBy = "composite")
Set<B> parts;
}
When toplink generates the DDL for this object hierarchy it creates all foreign key constraints as expected. However it does not set cascading rules for the constraints.
When I now try to delete an entire object graph via a reference to the A instance there can be situations where toplink fails to correctly remove the graph from the database. When toplink deletes a BC entity before deleting the contained B entities the foreign key constraint for the "composite" relationship is violated.
This situation can be corrected by manually adjusting the generated DDL to CASCADE (or SET NULL) on the relevant foreign key constraint which is fine for a production environment. This however fails in a test environment with in-memory (Derby) databases where DDL generation is managed entirely by toplink essentials and thus leads to the constraint violation described above.
Is there any way to influence the DDL generation process such that the required cascading rules are correctly set by toplink essentials?
Thanks for your help!
This is not an issue with DDL generation, but with deletion.
TopLink Essentials had some issues with resolving deletes from complex object graphs, or cyclic relationships. The are a few workarounds, such as deleting the dependent objects first and calling flush, then deleting the other objects, or setting the foreign key to null so they get updated. Using a customizer to mark the mapping privateOwned, or play with the constraint dependency may also work. You can also drop or defer the constraints.
All of the deletion issues have been fixed in EclipseLink, so upgrading the to latest EclipseLink release should resolve the issue.
EclipseLink also supports an #CascadeOnDelete annotation to add the cascade to the constraint in DDL generation.