I have 2 entities with many-to-many relationship. The Movie Entity is the owner of this relation, so when I want to delete an Actor Entity I use a method annotated @PreRemove to delete any occurrences of Actor ID in Movie cast to avoid "Foreign key violation exception".
Movie class
@Entity
public class Movie extends AbstractBusinessObject{
@ManyToMany
private Map<String, Actor> cast;
// setters and getters
public void removeCastMember(Actor actor){
for (Entry<String, Actor> e : cast.entrySet()) {
if(e.getValue().id.equals(actor.id)){
cast.remove(e.getKey());
}
}
} // removeCastMember()
}
Actor class
@Entity
public class Actor extends AbstractBusinessObject{
@ManyToMany(mappedBy = "cast")
private Set<Movie> movies;
// setters and getters
@PreRemove
private void removeActorFromMovies() {
for (Movie m : movies) {
m.removeCastMember(this);
}
}
}
To be clear, from my testing, it works - movie objects are correctly updated in the database. However, I cannot understand how is it possible when there are no calls to saveOrUpdate() or persist/merge those objects.
JPA's 4 Lifecycle States. The lifecycle model consists of the 4 states transient, managed, removed, and detached.
JPA Relationship TypesEditManyToOne - A reference from one object to another, inverse of a OneToMany . OneToMany - A Collection or Map of objects, inverse of a ManyToOne . ManyToMany - A Collection or Map of objects, inverse of a ManyToMany . Embedded - A reference to a object that shares the same table of the parent.
Annotation Type PreRemoveSpecifies a callback method for the corresponding lifecycle event. This annotation may be applied to methods of an entity class, a mapped superclass, or a callback listener class. Since: Java Persistence 1.0.
That's a fundamental feature of JPA/Hibernate. All the changes made to attached entities are automatically made persistent: Hibernate manages them, so it compares their current state with their initial state, and automatically makes all the changes persistent.
This is extremely useful, because you don't have to track all the entities that have been modified in a complex business method modifying lots of entities. And it's also efficient because Hibernate won't execute unnecessary SQL: if an entity hasn't changed during the transaction, no SQL update query will be executed for this entity. And if you modify entities and then throw an exception rollbacking the transaction, Hibernate will skip the updates.
So, typical JPA code would look like this:
void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
Account from = em.find(Account.class, fromAccountId); // from is managed by JPA
Account to = em.find(Account.class, ftoAccountId); // to is managed by JPA
from.remove(amount);
to.add(amount);
// now the transaction ends, Hibernate sees that the state of from and to
// has changed, and it saves the entities automatically before the commit
}
persist()
is used to make a new entity persistent, i.e. to make it managed by Hibernate.
merge()
is used to take a detached entity (i.e. an entity which is not managed by Hibernate, but already has an ID and a state) and to copy its state to the attached entity having the same ID.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With