Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to pass detached object to JPA persist? (detached entity passed to persist)

I have 2 entities : Account and AccountRole.

public class Account {
   private AccountRole accountRole;

   @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
   public AccountRole getAccountRole() {
      return accountRole;
   }

.

public class AccountRole {
    private Collection<Account> accounts = new ArrayList<Account>();

    @OneToMany(mappedBy = "accountRole", fetch = FetchType.EAGER)
    public Collection<Account> getAccounts() {
         return accounts;
    }

Problem comes when I take the accountRole from database and try to persist my Account. At this point I just created my account and role already exists in db.

AccountRole role = accountService.getRoleFromDatabase(AccountRoles.ROLE_USER);
account.setAccountRole(role);

//setting both ways, as suggested
public void setAccountRole(AccountRole accountRole) {
    accountRole.addAccount(this);
    this.accountRole = accountRole;
}

entityManager.persist(account); // finally in my DAO

I read this : JPA/Hibernate: detached entity passed to persist And what I understood, I must set the entities values from both direction, so that what I am doing in my setter.

Still getting error.

 org.hibernate.PersistentObjectException: detached entity passed to persist: foo.bar.pojo.AccountRole
like image 592
Jaanus Avatar asked Dec 12 '12 08:12

Jaanus


People also ask

How do you resolve a detached entity passed to persist?

The solution is simple, just use the CascadeType. MERGE instead of CascadeType. PERSIST or CascadeType. ALL .

What is detached entity passed to persist?

A detached entity (a.k.a. a detached object) is an object that has the same ID as an entity in the persistence store but that is no longer part of a persistence context (the scope of an EntityManager session).

How do you persist a detached object in hibernate?

Detached instances may be made persistent by calling update() , saveOrUpdate() , lock() or replicate() . The state of a transient or detached instance may also be made persistent as a new persistent instance by calling merge() .


2 Answers

Just replace the

entityManager.persist(account);

with:

entityManager.merge(account);

And allow merge cascading:

@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER)
public AccountRole getAccountRole() {
    return accountRole;
}

Because merge does this:

If your entity is new, it's the same as a persist(). But if your entity already exists, it will update it.

like image 171
Jaanus Avatar answered Oct 24 '22 05:10

Jaanus


It looks like you leave the transaction during your processing, so the accountRole gets detached, or it is already detached for other reasons.

A call to entityManager.merge(accountRole) before calling entityManager.persist(account) should fix it.

EDIT: Unfortunately, if you cannot be sure if the accountRole already exists in the DB, you will have to check it by querying. If it exists - merge, if not - persist. It is indeed a hassle, but I have not yet seen a better workaround.

EDIT2: The entity you pass to the merge method will remain detached - the managed entity will be returned by the merge, so you would need to merge first, then set the reference on the account to the return value of the merge.

like image 33
kostja Avatar answered Oct 24 '22 07:10

kostja