2009-07-07 12 views
35

ho una classe denominata SynonymMapping che ha una collezione di valori mappati come CollectionOfElementsHibernate CollectionOfElements EAGER fetch duplicati elementi

@Entity(name = "synonymmapping") 
public class SynonymMapping { 

    @Id private String keyId; 

    //@CollectionOfElements(fetch = FetchType.EAGER) 
    @CollectionOfElements 
    @JoinTable(name="synonymmappingvalues", joinColumns={@JoinColumn(name="keyId")}) 
    @Column(name="value", nullable=false) 
    @Sort(type=SortType.NATURAL) 
    private SortedSet<String> values; 

    public SynonymMapping() { 
     values = new TreeSet<String>(); 
    } 

    public SynonymMapping(String key, SortedSet<String> values) { 
     this(); 
     this.keyId = key; 
     this.values = values; 
    } 

    public String getKeyId() { 
     return keyId; 
    } 

    public Set<String> getValues() { 
     return values; 
    } 
} 

Ho un test in cui devo conservare due oggetti SynonymMapping al database e poi chiedere il database per restituire tutti gli oggetti di SynonymMapping salvati, aspettandosi di ricevere i due oggetti che ho archiviato.

Quando cambio la mappatura dei valori per essere desiderosa (come mostrato nel codice dalla riga commentata) ed eseguo di nuovo il test, ricevo quattro corrispondenze.

Ho eliminato il database tra le esecuzioni e posso duplicare questo problema scambiando tra desideroso e pigro.

Penso che abbia a che fare con i join che l'ibernazione crea sotto ma non riesco a trovare una risposta definitiva online.

Qualcuno può dirmi perché un recupero folle sta duplicando gli oggetti?

Grazie.

+0

Ogni persona con l'eccezione "Più di una riga con l'identificatore specificato è stata trovata" deve essere informata di ciò. Risparmia davvero molte ore senza sapere che diavolo sta andando male. Vedi @ user176668 risposta !! –

risposta

27

Generalmente non è una buona idea applicare forzatamente il recupero nella mappatura - è meglio specificare join desiderosi nelle query appropriate (a meno che tu non sia sicuro al 100% che in qualsiasi circostanza il tuo oggetto non abbia senso/essere valido senza che la raccolta venga popolata).

Il motivo per cui si ottengono duplicati è perché Hibernate collega internamente le tabelle radice e di raccolta. Si noti che in realtà sono duplicati, ad es. per 2 SynonymMappings con 3 elementi di raccolta ciascuno otterresti 6 risultati (2x3), 3 copie di ciascuna entità SynonymMapping. Quindi la soluzione più semplice è quella di avvolgere i risultati in un Set assicurando che siano unici.

+34

Ma perché Hibernate non li può filtrare, non riesco a capire perché lo vorresti mai così così. –

+0

Posso confermare che funziona. Stavo usando una collezione solo in generale, ed è stato un errore. –

2

Si potrebbe utilizzare una clausola SELECT DISTINCT (Hibernate Query Language) come segue

SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values 

clausola DISTINCT rimuove i riferimenti duplicati in Hibernate.

Sebbene sia la raccolta di componenti sia quella di tipo di valore abbiano il proprio ciclo di vita associato alla classe di entità proprietaria, è necessario dichiararli nella clausola select per recuperarli. (LEFT JOIN FETCH synonym.values)

risposta di ChssPly76 è un altro approccio, ma non dimentica di override uguale e il metodo codice hash in base per impostare semantiche

saluti,

+3

Mi chiedevo di più sul perché questo accadesse e sul perché la decisione progettuale fosse presa perché il letargo rispondesse in questo modo più che i diversi modi per aggirarlo. – Rachel

62

ho fatto un passo nello stesso problema - quando si imposta FetchType.EAGER per un @CollectionOfElements, Hibernate prova a ottenere tutto in un colpo, cioè utilizzando una singola query per ogni voce dell'elemento collegato a un oggetto "master". Questo problema può essere risolto con successo a un costo della query N + 1, se si aggiunge l'annotazione @Fetch (FetchMode.SELECT) alla raccolta. Nel mio caso volevo avere un'entità MediaObject con una raccolta dei suoi elementi di metadati (codec video, codec audio, dimensioni, ecc.).La mappatura per una collezione metadataItems si presenta come segue:

 

@CollectionOfElements (targetElement = String.class, fetch = FetchType.EAGER) 
@JoinTable(name = "mo_metadata_item", joinColumns = @JoinColumn(name = "media_object_id")) 
@MapKey(columns = @Column(name = "name")) 
@Column (name = "value") 
@Fetch (FetchMode.SELECT) 
private Map<String, String> metadataItems = new HashMap<String, String>(); 
+2

Grazie mille per questo. – AHungerArtist

+1

Mi è piaciuta la soluzione, perché non abbiamo bisogno di avvolgere il risultato con un Set per ottenere unicità. – sanbhat

+0

Grazie mille per questo !!! Salvato la mia giornata :) – pasql

5

Ho affrontato questo problema e ho risolto utilizzando

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Questo cancella i duplicati che sono causati dalla join fatta al tavoli per bambini.

0

Invece di FetchMode.SELECT con query N + 1 è preferibile utilizzare BatchSize e.q. @BatchSize(size = 200).

DISTINCT e Criteria.DISTINCT_ROOT_ENTITY non aiuta, se è necessario recuperare più di 1 associazione. Per questo caso vedere altre soluzioni: https://stackoverflow.com/a/46013654/548473

Problemi correlati