JPA: Cascading on shared objects - jpa

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.

Related

After delete Member the Relation is not deleted

I have a problem with orphanRemoval. When I Delete Member and if there is no others Members referenced relation given, this relation should be deleted. But this is not the case. There is the moyen to do this with JPA configs, or I should write logic in Service ?
public class Member {
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public RelationEntity getBornRelation() {
return bornRelation;
}
}
public class Relation {
#OneToMany(fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
#JoinColumn(name = "bornrelation")
public List<MemberEntity> getChildren() {
return children;
}
}
First of all, you've defined two separate associations between Member and Relation. I suspect that wasn't intentional. If you want to keep the association bidirectional, you need to make the 'one' side the owning side of the association by adding mappedBy="bornRelation" to the #OneToMany. Otherwise, changes to Member.bornRelation will not be reflected in Relation.children and vice versa.
Secondly, do not use CascadeType.ALL with #ManyToOne. CascadeType.ALL implies CascadeType.REMOVE and an attempt to remove the 'one' side of the association will fail as long as the 'many' side still contains other child entities that refer to the parent entity. JPA will not check if the deletion is possible. It will simply issue a DELETE to the database, resulting in constraint violations.
(you can keep using CascadeType.ALL with #OneToMany, but remember that you still need to make sure Member.bornRelation is set properly when adding new elements to Relation.children. JPA will not take care of that for you)
I believe the above already answers your question. What you want cannot be done by JPA configuration only, you need additional logic to check whether the Relation to delete is the last child of its parent entity. This, by the way, has nothing to do with orphan removal. Orphan removal is about removing child entities (and not parent entities).

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.

Multiple representations of the same entity are being merged, Detached

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

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;
....

JPA: Unidirectional ManyToMany with existing object to be inserted

User {
Set<Book> _books;
#ManyToMany(cascade={CascadeType.MERGE})
#JoinTable(
name="BLETAG_LABEL",
joinColumns={#JoinColumn(name="TAG_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="LABEL_ID", referencedColumnName="ID")})
Set<Book>getBooks();
}
Book {
}
. If I set the CascadeType to PERSIST and I try to create a User with an existing book (book1) like this, an unique constraint is fired...
Book book1 = new Book("book1");
user.getBooks().add(book1);
. My current solution is to the the CascadeType to MERGE and check for all user.getBooks() if the book already exist. If the book does not exist, I need to create it (because not CascadeType.PERSIST)
Is it the right solution ?
Is there any better "automatic" solution available for this kind of use case ?
As long as you use the Book from the current persistence context, cascade persist should work fine, and only persist the new Books. If your Book is detached, then you first need to merge or find it in the current persistence context.