java - cascadetype - IllegalStateException con Hibernate 4 y ManyToOne en cascada
orphanremoval (9)
¿Su relación entre el artículo y el componente es unidireccional o bidireccional ? Si es bidireccional, asegúrese de no hacer que Cascade.MERGE
llamadas Cascade.MERGE
vuelvan al Item.
Básicamente, la versión más nueva de Hibernate tiene un mapa de entidad que contiene una lista de todas las cosas que necesitan fusionarse según la llamada a fusionar () llamará a fusión y luego pasará a la siguiente, pero mantendrá las cosas en el mapa, arrojará el error que indica más arriba "Una copia de entidad ya fue asignada a una entidad diferente" cuando encuentra un elemento que ya ha sido tratado. Encontramos en nuestra aplicación cuando ubicamos estas fusiones "hacia arriba" en el gráfico de objetos, es decir. en enlaces bidireccionales, arregló la llamada de combinación.
Tengo esas dos clases
Objeto MyItem:
@Entity
public class MyItem implements Serializable {
@Id
private Integer id;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Component defaultComponent;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Component masterComponent;
//default constructor, getter, setter, equals and hashCode
}
Objeto componente:
@Entity
public class Component implements Serializable {
@Id
private String name;
//again, default constructor, getter, setter, equals and hashCode
}
Y estoy tratando de persistir con el siguiente código:
public class Test {
public static void main(String[] args) {
Component c1 = new Component();
c1.setName("comp");
Component c2 = new Component();
c2.setName("comp");
System.out.println(c1.equals(c2)); //TRUE
MyItem item = new MyItem();
item.setId(5);
item.setDefaultComponent(c1);
item.setMasterComponent(c2);
ItemDAO itemDAO = new ItemDAO();
itemDAO.merge(item);
}
}
Si bien esto funciona bien con Hibernate 3.6, Hibernate 4.1.3 lanza
Exception in thread "main" java.lang.IllegalStateException: An entity copy was already assigned to a different entity.
at org.hibernate.event.internal.EventCache.put(EventCache.java:184)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:285)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:914)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:896)
at org.hibernate.engine.spi.CascadingAction$6.cascade(CascadingAction.java:288)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:213)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:282)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:904)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:888)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:892)
at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:874)
at sandbox.h4bug.Test$GenericDAO.merge(Test.java:79)
at sandbox.h4bug.Test.main(Test.java:25)
El back-end de la base de datos es h2 (pero lo mismo ocurre con hsqldb o derby). ¿Qué estoy haciendo mal?
De acuerdo con la lógica en EventCache, todas las entidades en el gráfico de objetos deben ser únicas. Entonces, la mejor solución (¿o es que funciona?) Es eliminar la cascada en MyItem para Component. Y combine el Componente por separado si es realmente necesario. Apuesto a que en el 95% de los casos el Componente no debería fusionarse de acuerdo con la lógica comercial.
Por otro lado, realmente me interesaba conocer los verdaderos pensamientos detrás de esa restricción.
Intente agregar la anotación @GeneratedValue
bajo @Id
en la clase Componente. de lo contrario, dos instancias diferentes podrían obtener la misma identificación y colisionar.
Parece que les estás dando la misma identificación.
Component c1 = new Component();
c1.setName("comp");
Component c2 = new Component();
c2.setName("comp");
Eso podría resolver tu problema.
Lo mismo aquí, verifica el método equals (). Lo más probable es que esté mal implementado.
Editar: he verificado que una operación de fusión no funcionará si no implementa correctamente los métodos equals () y hashCode () de su Entidad.
Debe seguir estas pautas para implementar equals () y hashCode ():
"Se recomienda que implemente equals () y hashCode () usando igualdad de clave comercial. La igualdad de clave comercial significa que el método equals () compara solo las propiedades que forman la clave de negocios. Es una clave que identificaría nuestra instancia en el mundo real (una clave candidata natural) "
Eso significa: ¡NO debe usar su Id como parte de la implementación de equals ()!
Si el nombre es Id, ¿por qué creas dos objetos con el mismo ID? puedes usar el objeto c1 en todo el código.
Si eso es solo un ejemplo y creas el objeto c2 en otra parte del código, entonces no deberías crear un objeto nuevo sino cargarlo desde la base de datos:
c2 = itemDao.find("comp", Component.class); //or something like this AFTER the c1 has been persisted
Tenía la misma excepción (hibernación 4.3.0.CR2) agotador para guardar un objeto que está teniendo dos copias de un objeto secundario, fue arreglado por, en la entidad desde:
@OneToOne(cascade = CascadeType.MERGE)
private User reporter;
@OneToOne(cascade = CascadeType.MERGE)
private User assignedto;
para sólo,
@OneToOne
private User reporter;
@OneToOne
private User assignedto;
No sé la razón, aunque
Tuve el mismo problema, lo resolví. Si bien las respuestas anteriores pueden resolver el problema, no estoy de acuerdo con algunas de ellas, especialmente al modificar los métodos equlas () y hashcode () implementados. Sin embargo, siento que mi respuesta refuerza las respuestas de @Tobb y @Supun s ''.
En mi lado Muchos (lado infantil) tuve
@OneToMany(mappedBy = "authorID", cascade =CascadeType.ALL, fetch=FetchType.EAGER)
private Colllection books;
Y por mi parte (lado de los padres)
@ManyToOne(cascade =CascadeType.ALL)
private AuthorID authorID;
Después de leer la excelente respuesta superior proporcionada por @Tobb y un poco de pensamiento, me di cuenta de que las anotaciones no tenían sentido. La forma en que lo entiendo (en mi caso) estaba fusionando () el objeto Autor y fusionando () el libro Objeto. Pero debido a que la colección de libros es un componente del objeto Autor, intentaba guardarlo dos veces. Mi solución fue cambiar los tipos de cascada a:
@OneToMany(mappedBy = "authorID", cascade =CascadeType.PERSIST, fetch=FetchType.EAGER)
private Collection bookCollection;
y
@ManyToOne(cascade =CascadeType.MERGE)
private AuthorID authorID;
Para abreviar, persista el objeto padre y combine el objeto hijo.
Espero que esto ayude / tenga sentido.
Tuve el mismo problema, y esto es lo que encontré:
El método de fusión atraviesa el gráfico del objeto que desea almacenar, y para cada objeto en este gráfico lo carga desde la base de datos, por lo que tiene un par de (entidad persistente, entidad separada) para cada objeto en el gráfico, donde entidad separada es la entidad que se va a almacenar y la entidad persistente se obtiene de la base de datos. (En el método, así como en el mensaje de error, la entidad persistente se conoce como ''copia''). Luego estos pares se colocan en dos mapas, uno con la entidad persistente como clave y la entidad separada como valor, y uno con la entidad separada como clave y la entidad persistente como valor.
Para cada par de entidades, comprueba estos mapas, para ver si la entidad persistente se asigna a la misma entidad separada que antes (si ya se ha visitado), y viceversa. Este problema ocurre cuando obtienes un par de entidades donde hacer un get con la entidad persistente devuelve un valor, pero un obtener desde el otro mapa, con la entidad separada devuelve nulo, lo que significa que ya has vinculado la entidad persistente con una entidad separada entidad con un hashcode diferente (básicamente el identificador de objeto si no ha reemplazado el método hashcode).
TL; DR, tiene múltiples objetos con diferentes identificadores de objeto / código hash, pero con el mismo identificador de persistencia (haciendo referencia a la misma entidad persistente). Aparentemente, esto ya no está permitido en las versiones más nuevas de Hibernate4 (4.1.3.Final y superior a lo que pude ver).
El mensaje de error no es muy bueno, pero lo que realmente debería decir es algo así como:
A persistent entity has already been assigned to a different detached entity
o
Multiple detached objects corresponding to the same persistent entity
si está utilizando jboss EAP 6 ... cámbielo a jboss 7.1.1. Este es un error de jboss EAP 6. https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.3/html/6.3.0_Release_Notes/ar01s07s03.html