PersistentObjectException: entidad separada pasada para persistir lanzada por JPA e Hibernate
Tengo un modelo de objetos persistentes JPA que contiene una relación de muchos a uno: an Account
has many Transactions
. A Transaction
tiene uno Account
.
Aquí hay un fragmento del código:
@Entity
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
private Account fromAccount;
....
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
private Set<Transaction> transactions;
Puedo crear un Account
objeto, agregarle transacciones y conservar el Account
objeto correctamente. Pero, cuando creo una transacción, usando una Cuenta existente ya persistente y persistiendo la Transacción , obtengo una excepción:
Causado por: org.hibernate.PersistentObjectException: entidad separada pasada para persistir: com.paulsanwald.Account en org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)
Por lo tanto, puedo conservar una transacción Account
que contiene transacciones, pero no una transacción que tiene un archivo Account
. Pensé que esto se debía a que es Account
posible que no esté adjunto, pero este código aún me da la misma excepción:
if (account.getId()!=null) {
account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
// the below fails with a "detached entity" message. why?
entityManager.persist(transaction);
¿ Cómo puedo guardar correctamente un archivo asociado a un objeto Transaction
ya persistente ?Account
La solución es simple, simplemente use en CascadeType.MERGE
lugar de CascadeType.PERSIST
o CascadeType.ALL
.
He tenido el mismo problema y CascadeType.MERGE
me ha funcionado.
Espero que estés ordenado.
Este es un problema típico de coherencia bidireccional. Está bien discutido en este enlace así como en este enlace.
Según los artículos de los 2 enlaces anteriores, debe corregir sus configuradores en ambos lados de la relación bidireccional. Un configurador de ejemplo para el lado Uno está en este enlace.
Un creador de ejemplo para el lado Muchos se encuentra en este enlace.
Después de corregir sus configuradores, desea declarar que el tipo de acceso a la entidad sea "Propiedad". La mejor práctica para declarar el tipo de acceso "Propiedad" es mover TODAS las anotaciones de las propiedades del miembro a los captadores correspondientes. Una gran advertencia es no mezclar los tipos de acceso "Campo" y "Propiedad" dentro de la clase de entidad, de lo contrario, el comportamiento no está definido por las especificaciones JSR-317.
Eliminación de la asociación infantil en cascada
Por lo tanto, debe eliminarlo @CascadeType.ALL
de la @ManyToOne
asociación. Las entidades secundarias no deben conectarse en cascada a las asociaciones principales. Sólo las entidades principales deben conectarse en cascada a entidades secundarias.
@ManyToOne(fetch= FetchType.LAZY)
Observe que configuré el fetch
atributo en FetchType.LAZY
porque la búsqueda ansiosa es muy mala para el rendimiento.
Estableciendo ambos lados de la asociación
Siempre que tenga una asociación bidireccional, deberá sincronizar ambos lados usando los métodos addChild
y removeChild
en la entidad principal:
public void addTransaction(Transaction transaction) {
transcations.add(transaction);
transaction.setAccount(this);
}
public void removeTransaction(Transaction transaction) {
transcations.remove(transaction);
transaction.setAccount(null);
}