IllegalStateException con Hibernate 4 e ManyToOne a cascata

Ho queste due classi

Oggetto 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 } 

Oggetto componente:

 @Entity public class Component implements Serializable { @Id private String name; //again, default constructor, getter, setter, equals and hashCode } 

E sto cercando di persistere con il seguente codice:

 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); } } 

Mentre questo funziona bene con Hibernate 3.6, i lanci di Hibernate 4.1.3

     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) 

    Il back-end del database è h2 (ma lo stesso accade con hsqldb o derby). Che cosa sto facendo di sbagliato?

    Ho avuto lo stesso problema, e questo è quello che ho trovato:

    Il metodo di unione attraversa il grafico dell’object che si desidera memorizzare e per ogni object in questo grafico lo carica dal database, quindi ha una coppia di (entity framework permanente, quadro distaccata) per ciascun object nel grafico, dove quadro distaccata è l’ quadro che verrà memorizzata e l’ quadro persistente viene acquisita dal database. (Nel metodo, così come nel messaggio di errore, l’ quadro persistente è nota come ‘copia’). Quindi queste coppie sono messe in due mappe, una con l’ quadro persistente come chiave e l’ quadro distaccata come valore, e una con l’entity framework distaccata come chiave e l’ quadro persistente come valore.

    Per ciascuna di queste coppie di entit, controlla queste mappe, per vedere se l’ quadro persistente esegue il mapping sulla stessa entity framework distaccata di prima (se è già stata visitata) e viceversa. Questo problema si verifica quando ottieni una coppia di entity framework in cui ottenere un get con l’ quadro persistente restituisce un valore, ma un get dall’altra mappa, con l’ quadro distaccata restituisce null, il che significa che hai già collegato l’entity framework persistente con una distaccata entity framework con un hashcode diverso (in pratica l’identificativo dell’object se non si è sovrascritto il metodo hashcode).

    TL; DR, hai più oggetti con identificatori / codici di oggetti diversi, ma con lo stesso identificatore di persistenza (facendo così riferimento alla stessa quadro persistente). Questo non è più consentito nelle nuove versioni di Hibernate4 (4.1.3.Finale e ascendente da quello che potrei dire).

    Il messaggio di errore non è molto bello, quello che dovrebbe davvero dire è qualcosa di simile:

    A persistent entity has already been assigned to a different detached entity

    o

    Multiple detached objects corresponding to the same persistent entity

    Lo stesso qui, controlla il tuo metodo equals (). Molto probabilmente è mal implementato.

    Modifica: ho verificato che un’operazione di unione non funzionerà se non si implementano correttamente i metodi equals () e hashCode () di Entity.

    Dovresti seguire queste linee guida per l’implementazione di equals () e hashCode ():

    http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch04.html#persistent-classs-equalshashcode

    “Si consiglia di implementare equals () e hashCode () utilizzando l’uguaglianza della chiave Business. L’uguaglianza delle chiavi di business significa che il metodo equals () confronta solo le proprietà che formano la business key. È una chiave che identificherebbe la nostra istanza nel mondo reale (una chiave di candidatura naturale) “

    Ciò significa: NON dovresti usare il tuo ID come parte dell’implementazione di equals ()!

    La tua relazione tra articolo e componente è unidirezionale o bidirezionale ? Se è bidirezionale, assicurati di non avere chiamate Cascade.MERGE risalenti Cascade.MERGE .

    Fondamentalmente, la versione più recente di Hibernate ha una mappa delle entity framework che contiene un elenco di tutte le cose che devono essere unite in base alla chiamata per unire () chiamerà fusione e quindi passerà alla successiva, ma manterrà le cose nella mappa, genererà l’errore che hai indicato sopra “Una copia di quadro è stata già assegnata a un’altra entity framework” quando incontra un object che è già stato trattato. Abbiamo trovato nella nostra app quando abbiamo individuato queste fusioni “al rialzo” nel grafico dell’object, ad esempio. su collegamenti bidirezionali, ha risolto la chiamata di unione.

    Ha avuto la stessa eccezione (ibernazione 4.3.0.CR2) stancante per salvare un object che ha due copie di un object figlio, risolto da, nell’ quadro da:

     @OneToOne(cascade = CascadeType.MERGE) private User reporter; @OneToOne(cascade = CascadeType.MERGE) private User assignedto; 

    solo per,

     @OneToOne private User reporter; @OneToOne private User assignedto; 

    non so la ragione però

    Prova ad aggiungere l’annotazione @GeneratedValue nella class Component. altrimenti due diverse istanze potrebbero ottenere lo stesso id e collidere.

    Sembra che tu stia dando loro lo stesso ID.

      Component c1 = new Component(); c1.setName("comp"); Component c2 = new Component(); c2.setName("comp"); 

    Potrebbe risolvere il tuo problema.

    Se il nome è l’ID, perché stai creando due oggetti con lo stesso id ?? puoi usare l’object c1 in tutto il codice.

    Se questo è solo un esempio e crei l’object c2 in un’altra parte del codice, non dovresti creare un nuovo object ma caricarlo dal database:

     c2 = itemDao.find("comp", Component.class); //or something like this AFTER the c1 has been persisted 

    Secondo la logica in EventCache, tutte le quadro nel grafo degli oggetti dovrebbero essere uniche. Quindi la soluzione migliore (o funziona?) È rimuovere la cascata in MyItem su Component. E unisci separatamente Componente se è davvero necessario. Scommetto che nel 95% dei casi Componente non dovrebbe essere unito secondo la logica aziendale.

    D’altra parte – mi interessa davvero conoscere i veri pensieri dietro quella restrizione.

    se si utilizza jboss EAP 6 .. modificarlo in jboss 7.1.1. questo è un bug di jboss EAP 6. https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.3/html/6.3. 0_Release_Notes / ar01s07s03.html

    Ho avuto lo stesso problema, ho appena risolto. Mentre le risposte di cui sopra possono risolvere il problema, non sono d’accordo con alcune di esse, specialmente modificando i metodi equlas () e hashcode () implementati. Tuttavia ritengo che la mia risposta rafforzi la risposta di @Tobb e @Supun s.

    Dal mio lato Many (lato bambino) che ho avuto

      @OneToMany(mappedBy = "authorID", cascade =CascadeType.ALL, fetch=FetchType.EAGER) private Colllection books; 

    E da parte mia (lato genitore)

      @ManyToOne(cascade =CascadeType.ALL) private AuthorID authorID; 

    Dopo aver letto l’eccellente risposta fornita da @Tobb e un po ‘di riflessione, mi sono reso conto che le annotazioni non avevano senso. Il modo in cui ho capito (nel mio caso) stavo unendo () l’object Autore e unendo () l’object del libro. Ma poiché la raccolta di libri è un componente dell’object Autore, stava tentando di salvarla due volte. La mia soluzione era di cambiare i tipi di cascata in:

      @OneToMany(mappedBy = "authorID", cascade =CascadeType.PERSIST, fetch=FetchType.EAGER) private Collection bookCollection; 

    e

      @ManyToOne(cascade =CascadeType.MERGE) private AuthorID authorID; 

    Per fare una lunga storia breve, Persistere l’object genitore e unire l’object figlio.

    Spero che questo aiuti / abbia senso.