Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA - @PreRemove method behaviour

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.

like image 639
Kuba Spatny Avatar asked Nov 03 '13 09:11

Kuba Spatny


People also ask

What is the correct sequence of JPA life cycle?

JPA's 4 Lifecycle States. The lifecycle model consists of the 4 states transient, managed, removed, and detached.

What are the different types of relationships in JPA?

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.

What is @PreRemove?

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.


1 Answers

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.

like image 109
JB Nizet Avatar answered Sep 20 '22 18:09

JB Nizet