2011-12-14 10 views
14

Ho trovato severalquestionsabout questo, ma nessuno con una spiegazione completa del problema, e come eseguire il debug - le risposte sono tutte aneddotiche.Come eseguire il debug di "Trovato due rappresentazioni della stessa collezione"?

Il problema è che in un gioco 1.2.4 test di APP, sto ottenendo questa eccezione quando ho save() un modello:

org.hibernate.HibernateException: Trovato due rappresentazioni della stessa collezione : modelli .Position.projects

Vorrei sapere:

  1. c'è una documentazione di questo problema, in generale, un collegato a Play? Il problema è in ibernazione, tuttavia molti dei risultati di Google su questo sono all'interno delle app Play.
  2. Quali sono alcune best practice di base per evitare questo problema?
  3. È causato da Play? O qualcosa che sto sbagliando?
  4. Come risolvere nel mio caso specifico?

Here is a reproduction of the problem on github. Ho quattro entità:

@Entity 
public class Person extends Model { 
    public String name; 

    @OneToMany(cascade = CascadeType.ALL) 
    public List<Position> positions; 
} 


@Entity 
public class Position extends Model { 
    public Position(){} 
    public Position(Company companies) { 
     this.companies = companies; 
     this.projects = new ArrayList<Project>(); 
    } 

    @OneToOne 
    public Company companies; 

    @ManyToOne 
    public Person person; 

    @OneToMany 
    public List<Project> projects; 
} 

@Entity 
public class Company extends Model { 
    public String name; 
} 

@Entity 
public class Project extends Model { 
    public Project(){} 
    public Project(String field, String status){ 
     this.theField = field; 
     this.status = status; 
    } 

    @ManyToOne 
    public Position position; 

    public String theField; 
    public String status; 
} 

e il mio codice di persistenza:

Company facebook = new Company(); 
facebook.name = "Facebook"; 
facebook.save(); 
Company twitter = new Company(); 
twitter.name = "Twitter"; 
twitter.save(); 

Person joe = new Person(); 
joe.name = "Joe"; 
joe.save(); 

joe.positions = new ArrayList<Position>(); 

Position joeAtFacebook = new Position(facebook); 
joeAtFacebook.projects.add(new Project("Stream", "Architect")); 
joeAtFacebook.projects.add(new Project("Messages", "Lead QA")); 
joe.positions.add(joeAtFacebook); 

Position joeAtTwitter = new Position(twitter); 
joeAtTwitter.projects.add(new Project("Steal stuff from Facebook", "CEO")); 
joe.positions.add(joeAtTwitter); 
joe.save(); 

A proposito, ho provato ad aggiungere la Play associations module come una sola persona ha suggerito, e does't sembra aiutare.

vedo che, infatti, che le tabelle che vengono create sono duplicati in un certo senso:

Ho sia un tavolo person_position e un position table, dove entrambi contengono campi simili: person_position contiene un Person_id e positions_id, mentre il tavolo position contenere id (che indica l'ID di posizione), person_id e companies_id. Quindi capisco che una sorta di ridondanza involontaria è stata creata dalla mia definizione del modello, ma non capisco davvero come risolverlo.

ho pensato che questo potrebbe essere correlato al mappature bidirezionali, ma qui è una branch where the model is uni-directional (ho tolto alcuni back-riferimenti) - e il problema si verifica ancora.

+0

la mia ipotesi - problema a cascata. aggiungi una posizione e la posizione aggiunge due progetti, quindi joe.save() potrebbe causare l'aggiunta della posizione due volte. Gioca con le opzioni Cascade e riporta nuovamente :) –

+0

@KenEgozi - le opzioni a cascata influiscono su come vengono generate le mie tabelle? Perché ho una duplicazione nella struttura della tabella stessa ... (Ho messo i dettagli nella domanda) – ripper234

+0

@KenEgozi - risolto aggiornando la versione di ibernazione - vedi la mia risposta. Grazie. – ripper234

risposta

7

Prova

 @OneToMany(mappedBy="position") 
        public List<Project> projects; 
+0

Questo era parte della soluzione, grazie. – ripper234

+1

Inoltre, se stai usando ** persist (entità) ** e tutti i tuoi @ OneToMany hanno già una proprietà "mappedBy", potresti provare a utilizzare ** merge (entity) **. – izilotti

9

Per quanto sono stato in grado di dire, l'errore è causato da una combinazione di:

  • Mancando/mancante mappedBy parametro @OneToMany annotazioni. Questo parametro dovrebbe ricevere il nome del campo nel modello di destinazione che fa riferimento a questo modello.
  • Old hibernate - Play 1.2.4 viene caricato con hibernate 3.6.1 ... l'aggiornamento a 3.6.8 sembra risolvere un altro problema di questo tipo (basta aggiungere quanto segue alle dipendenze.YML, e giocare dipendenze)

- org.hibernate -> hibernate-core 3.6.8.Final:

force: true

Per me, i passi sopra risolto il problema.

Si tratta infatti di un bug in ibernazione, perché viene generata quando persistente oggetti, mentre esso implica in realtà un problema "fase di progettazione" che dovrebbe essere rilevato durante la creazione dello schema.

passaggi che ho usato per eseguire il debug:

  • scritto un test che riproduce il problema
  • Aggiunta la associations module - non sono sicuro se si risolve una parte del problema, o messi peggio.
  • Debug attraverso il codice di ibernazione, e realizzato questo probabilmente indica un problema di ibernazione, non un errore di utente/configurazione.
  • Notato che Hibernate ha alcune versioni di bugfix dopo 3.6.1 e ha deciso di tentare la fortuna e l'aggiornamento.
  • Importante anche, pulire la cartella tmp non può far male - Riproduzione di cache di contenitori compilati lì, e dopo un cambiamento importante come l'aggiornamento della versione di ibernazione, potrebbe essere utile pulirlo.
+0

Non capisco perché hai indicato molte annotazioni su ManyToOne ... sembra strano che in una definizione per una singola entità tu stia specificando che molte di queste entità saranno correlate a un'altra singola entità. (Capisco il contrario.) ma penso che dovrebbe essere implicitamente compreso dal framework. (Non ho molta esperienza di hibrnate, lo ammetto) –

+0

@ItayLevin - Non sono sicuro del perché sia ​​esattamente richiesto, ma penso che sia lì per una buona ragione. Leggerò il tutorial sull'ibernazione e vedrò se sarò in seguito. – ripper234

1

In primo luogo credo che si dimentica di linea uno prima dell'ultima:

joe.positions.add(joeAtTwitter); 

Secondo: penso che non si dovrebbe fare

joe.positions = new ArrayList<Position>(); 

invece cambiare Person a:

@Entity 
public class Person extends Model { 
    public String name; 

    @OneToMany(cascade = CascadeType.ALL) 
    public List<Position> positions = new ArrayList<Position>(); 
} 

Sarà risolvi il problema, inoltre è una best practice, utilizzando la raccolta vuota anziché il valore null (vedere Java efficace) in generale e in particolare per il lavoro con oggetti gestiti di Hibernate. Leggere il primo paragrafo here per la spiegazione del motivo per cui è meglio inizializzare con le raccolte vuote.

Ora quello che penso è accaduto è: quando si chiama joe.save() aver effettuato l'oggetto gestito (da Hibernate) allora si sovrascritti una proprietà con una nuova collezione, non riesco a capire il motivo per cui l'errore che hai è di circa model.Position.projects, ma penso che sia così.

+0

Grazie per l'aiuto. Forse non sono stato chiaro nella mia risposta sopra, ma la specifica della proprietà mappedBy e l'aggiornamento da Hibernate 3.6.1 a 3.6.8 hanno risolto il problema per me. Ad ogni modo, ho risolto la mia domanda in base al tuo primo suggerimento. – ripper234

Problemi correlati