The solution is simple, just use the CascadeType. MERGE instead of CascadeType. PERSIST or CascadeType. ALL .
Detached Entities A detached entity is a Java object that is no longer tracked by the persistence context.
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).
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?
Don't pass id(pk) to persist method or try save() method instead of persist().
So, you need to remove the @CascadeType.ALL
from the @ManyToOne
association. Child entities should not cascade to parent associations. Only parent entities should cascade to child entities.
@ManyToOne(fetch= FetchType.LAZY)
Notice that I set the fetch
attribute to FetchType.LAZY
because eager fetching is very bad for performance.
Whenever you have a bidirectional association, you need to synchronize both sides using addChild
and removeChild
methods in the parent entity:
public void addTransaction(Transaction transaction) {
transcations.add(transaction);
transaction.setAccount(this);
}
public void removeTransaction(Transaction transaction) {
transcations.remove(transaction);
transaction.setAccount(null);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With