Dave
Dave

Reputation: 19160

How do I update a JPA field that has "orphanRemoval=true"?

I’m using JPA 2.0 with Hibernate 4.1.0.Final. I have this entity (uni-directional relationship between User and Address objects) …

@Entity
@Table(name = "user")
public class User implements Serializable
{

    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, orphanRemoval=true)
    @JoinTable(name = "user_address", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "address_id"))
    private Set<Address> addresses;

In my “updateUser” method, I’m have this code

        user.setAddresses(addresses);

which calls

public void setAddresses(Set<Address> addresses)
{
    if (this.addresses == null)
    {
        this.addresses = new HashSet<Address>();
    }
    this.addresses.clear();
    if (addresses != null)
    {
        this.addresses.addAll(addresses);
    }   // if
}

The problem is the addresses only save if the “addresses” are new objects, as opposed to re-saving existing Address entities. What do I need to do to update my user with existing addresses?

Edit: Here's how I save the entity ...

protected Object save(Object obj)
{
    if (obj != null)
    {
        final Object id = getId(obj);
        if (id == null)
        {
            m_entityManager.persist(obj);
        }
        else
        {
            obj = m_entityManager.merge(obj);
        }   // if
    }   // if
    return obj;
} // save

Upvotes: 0

Views: 3356

Answers (2)

Vlad Mihalcea
Vlad Mihalcea

Reputation: 153810

In order to save both the user and the addresses you need to call session.merge:

user.setAddresses(addresses);
session.merge(user);

The merge will be cascaded from User to Addresses, and it will work for both new Entities as for detached ones.

Also clearing the list and adding the elements back doesn't perform very well. i would recommend doing something like:

//always initialize the children ollections
private Set<Address> addresses = new HashSet<Address>();

//make sure equals/hashcode are implemented according to the business key semantics
//don't use the entity id for equals and hashcod

public void setAddresses(Set<Address> addresses) {  
    //1. remove the existing addresses that are not found in the new ones
    this.addresses.retainAll(addresses);
    //2. add the new addresses too, the common ones will be ignored by the Set semantics
    this.addresses.addAll(addresses);
}

Upvotes: 2

Luca Basso Ricci
Luca Basso Ricci

Reputation: 18403

Best pratice for Collection properties is to init collection to a new empty collection to let Hibernate proxify it and manage object removal in the right way.
Try change you code as

@GenericGenerator(name = "uuid-strategy", strategy = "org.collegeboard.springboard.core.util.SpringboardUUIDGenerator")
@Entity
@Table(name = "user")
public class User implements Serializable
{

    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, orphanRemoval=true)
    @JoinTable(name = "user_address", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "address_id"))
    private Set<Address> addresses = new HashSet<Address>;

Also look at Hibernate @ManyToOne only works with CascadeType.ALL for correct annotation/persistace methods pairs

Upvotes: 1

Related Questions