PersistentObjectException: detached entity passed to persist thrown by JPA and Hibernate

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

I have had the same problem and CascadeType.MERGE has worked for me.

I hope you are sorted.


This is a typical bidirectional consistency problem. It is well discussed in this link as well as this link.

As per the articles in the previous 2 links you need to fix your setters in both sides of the bidirectional relationship. An example setter for the One side is in this link.

An example setter for the Many side is in this link.

After you correct your setters you want to declare the Entity access type to be "Property". Best practice to declare "Property" access type is to move ALL the annotations from the member properties to the corresponding getters. A big word of caution is not to mix "Field" and "Property" access types within the entity class otherwise the behavior is undefined by the JSR-317 specifications.


Remove cascading from the child entity Transaction, it should be just:

@Entity class Transaction {
    @ManyToOne // no cascading here!
    private Account account;
}

(FetchType.EAGER can be removed as well as it's the default for @ManyToOne)

That's all!

Why? By saying "cascade ALL" on the child entity Transaction you require that every DB operation gets propagated to the parent entity Account. If you then do persist(transaction), persist(account) will be invoked as well.

But only transient (new) entities may be passed to persist (Transaction in this case). The detached (or other non-transient state) ones may not (Account in this case, as it's already in DB).

Therefore you get the exception "detached entity passed to persist". The Account entity is meant! Not the Transaction you call persist on.


You generally don't want to propagate from child to parent. Unfortunately there are many code examples in books (even in good ones) and through the net, which do exactly that. I don't know, why... Perhaps sometimes simply copied over and over without much thinking...

Guess what happens if you call remove(transaction) still having "cascade ALL" in that @ManyToOne? The account (btw, with all other transactions!) will be deleted from the DB as well. But that wasn't your intention, was it?