Thang Pham
Thang Pham

Reputation: 38705

JPA2.0: Delete Entity in OneToMany RelationShip

How do I delete an entity in a OneToMany relationship.

@Entity
@NamedQueries({
   @NamedQuery(name="User.findByUserNamePassword",
     query="select c from User c where c.userName = :userName AND c.password = :password")
})
@Table(name="\"USER\"")
public class User implements Serializable {
    @OneToMany(mappedBy="user", cascade=CascadeType.ALL, orphanRemove=true)
    private List<Profession> professions;

    public List<Profession> getProfessions() {
       return professions;
    }

    public void setProfessions(List<Profession> professions) {
       this.professions = professions;
    }

    public void addProfession(Profession profession){
       if(this.professions == null){
          this.professions = new ArrayList<Profession>();
       }
       this.professions.add(profession);
       profession.setUser(this);
    }

    public void removeProfession(Profession profession){
       if(this.professions != null){
          professions.remove(profession);
          profession.setUser(null);
       }
    }
}

Inside Profession Entity

@Entity
public class Profession implements Serializable {
    @ManyToOne
    @JoinColumn(name="UserId", nullable=false)
    private User user;

    public User getUser() {
       return user;
    }

    public void setUser(User user) {
       this.user = user;
    }

Then inside my EJB I have this

@Stateless
@LocalBean
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ScholarEJB{

    /**
     * Add a profession to a target user
     * @param user
     * @param profession
     */
    public void addProfession(User user, Profession profession){
        //Put the user in a managed state. It is important to do this before
        //adding a new profession onto user
        user = find(User.class, user.getId());
        user.addProfession(profession);
        this.create(user);   //This is persist action
    }

    public void removeProfession(User user, Profession profession){
        //Put the user in a managed state. It is important to do this before
        //adding a new profession onto user
        user = find(User.class, user.getId());
        user.remove(user);
        this.update(user);  //merge action
        //this.create(user) //also try this as well, but it does not work
    }
}

Now addProfession work beautifully, but removeProfession does not work. Not sure why? Help please. Do I need to evict caches?

Upvotes: 7

Views: 32632

Answers (5)

user2945730
user2945730

Reputation: 31

I just added orphanRemoval = true in the OneToMany relationship and I resolved it.

Class SolicitudRetorno:

@OneToMany(mappedBy = "solicitud", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
@NotAudited
private List<RetornoMenor> hijosRetorno;

Class RetornoMenor:

@OneToMany(mappedBy = "solicitud", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
@NotAudited
private List<RetornoMenor> hijosRetorno;

Upvotes: 2

James
James

Reputation: 18379

My guess as to what is occurring is that your User has a OneToMany relationship to Profession and you user object has the profession. When you delete the Profession the user still has the reference. Because the mapping is cascade persist, it re persists the Profession.

You need to ensure that you remove the profession from the user's professions before deleting it.

If you are using EclipseLink there is a property that may also help, but fixing your code to maintain your model correctly is the best solution. You could also remove the cascade persist.

"eclipselink.persistence-context.persist-on-commit"="false"

or, "eclipselink.persistence-context.commit-without-persist-rules"="true"

Upvotes: 6

Thang Pham
Thang Pham

Reputation: 38705

This is the solution to my original question, however, I do not know if this is the best

My EJB bean

@PersistenceContext(unitName="Bridgeye2-ejbPU")
private EntityManager em;

public <T> T create(T t) {
    em.persist(t);
    return t;
}

public <T> T find(Class<T> type, Object id) {
    return em.find(type, id);
}

public <T> void delete(T t) {
    t = em.merge(t);
    em.remove(t);
}

public <T> void removeAndClearCaches(T t){
    this.delete(t);
    clearCaches();
}

public <T> T update(T t) {
    return em.merge(t);    

Now in my Managed Bean, I do this

/**
 * Add a new profession 
 */
public void addNewProfession(){
    Profession profession = new Profession();        
    newProfessions.add(profession);        
}

/**
 * Remove the profession
 * @param profession
 */
public void removeProfession(Profession profession){
    //This will remove the `profession` of the list
    //at the presentation layer
    this.myProfessions.remove(profession);  
    //This will remove the `profession` of the list
    //at the persistence layer
    scholarEJB.removeAndClearCaches(profession);
}

Upvotes: 1

Edwin Dalorzo
Edwin Dalorzo

Reputation: 78569

If professions are only part of this relationship, then you can guarantee that when a profession is removed from the User's set it will also be removed from the database by turning on orphanRemoval on the OneToMany side of the relationship.

@OneToMany(mappedBy="user", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Profession> professions;

This is what the JPA 2.0 specification states

The JPA 2.0 specification states that

Associations that are specified as OneToOne or OneToMany support use of the orphanRemoval option. The following behaviors apply when orphanRemoval is in effect:

If an entity that is the target of the relationship is removed from the relationship (by setting the relationship to null or removing the entity from the relationship collection), the remove operation will be applied to the entity being orphaned. The remove operation is applied at the time of the flush operation. The orphanRemoval functionality is intended for entities that are privately "owned" by their parent entity. Portable applications must otherwise not depend upon a specific order of removal, and must not reassign an entity that has been orphaned to another relationship or otherwise attempt to persist it. If the entity being orphaned is a detached, new,or removed entity, the semantics of orphanRemoval do not apply.

If the remove operation is applied to a managed source entity, the remove operation will be cascaded to the relationship target in accordance with the rules of section 3.2.3, (and hence it is not necessary to specify cascade=REMOVE for the relationship)[20].

Upvotes: 13

mdma
mdma

Reputation: 57707

You might try clearing the user field in profession:

public void removeProfession(Profession profession){
       if(this.professions != null){
          professions.remove(profession);
          profession.setUser(null);  // disassociate profession from user
       }
    }

To be on the safe side, I would also check that the passed in profession's current user equals this, just in case someone passes in a profession belonging to another user.

Upvotes: 1

Related Questions