2009-07-07 17 views
35

Ich habe eine Klasse namens SynonymMapping, die als CollectionOfElements kartiert eine Sammlung von Werten hatHibernate CollectionOfElements EAGER holen Elemente Duplikate

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

Ich habe einen Test, wo ich speichern zwei SynonymMapping Objekte in die Datenbank und dann fragen die Datenbank um alle gespeicherten SynonymMapping-Objekte zurückzugeben, in der Erwartung, die zwei Objekte zu erhalten, die ich gespeichert habe.

Wenn ich die Zuordnung von Werten zu eifrig ändern (wie im Code durch die auskommentierte Zeile gezeigt) und den Test erneut ausführen, erhalte ich vier Übereinstimmungen.

Ich habe die Datenbank zwischen Läufen gelöscht, und ich kann dieses Problem zwischen eifrig und faul switching.

Ich denke, es hat mit den Joins zu tun, die Winterschlaf schafft darunter, aber ich kann keine definitive Antwort online finden.

Kann mir jemand sagen, warum ein eifriger Abruf die Objekte dupliziert?

Danke.

+0

Jeder mit der Ausnahme „Mehr als eine Zeile mit der angegebenen ID gefunden wurde“ soll darüber Bescheid wissen. Es spart wirklich viele Stunden nicht zu wissen, was zum Teufel schief geht. Siehe @ user176668 Antwort !! –

Antwort

27

Es ist im Allgemeinen keine gute Idee, im Mapping ein eifriges Abrufen zu erzwingen - es ist besser, eifrige Joins in geeigneten Abfragen anzugeben (es sei denn, Sie sind 100% sicher, dass Ihr Objekt unter keinen Umständen sinnvoll ist) gültig, ohne dass diese Sammlung ausgefüllt wird).

Der Grund, warum Duplikate erhalten, liegt daran, dass Hibernate intern deine Stamm- und Sammlungstabellen verbindet. Man beachte, dass es sich tatsächlich um Duplikate handelt, z. Für 2 SynonymMappings mit je 3 Sammlungselementen erhalten Sie 6 Ergebnisse (2x3), 3 Kopien jeder SynonymMapping-Entität. Die einfachste Problemumgehung besteht also darin, die Ergebnisse in einem Set zu verpacken, um sicherzustellen, dass sie eindeutig sind.

+34

Aber warum kann Hibernate diese nicht ausfiltern, ich kann nicht verstehen, warum du das jemals so willst. –

+0

Ich kann bestätigen, dass es funktioniert. Ich benutzte eine Sammlung nur im Allgemeinen, und das war ein Fehler. –

2

Sie eine SELECT DISTINCT (Hibernate Query Language) Klausel verwenden könnte als

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

DISTINCT-Klausel folgt entfernt doppelte Referenzen in den Ruhezustand.

Obwohl sowohl die Komponenten- als auch die Werttypauflistung an die Eigentümerentitätsklasse gebunden sind, sollten Sie sie in der SELECT-Klausel deklarieren, um sie abzurufen. (LEFT JOIN FETCH synonym.values)

ChssPly76 Antwort ist ein weiterer Ansatz, aber nicht vergessen, Überschreibung gleich und hashcode Verfahren nach Set semantischen

Grüßen,

+3

Ich frage mich mehr darüber, warum dies passiert und warum die Designentscheidung getroffen wurde, damit der Winterschlaf so mehr reagiert als die verschiedenen Möglichkeiten, um es zu umgehen. – Rachel

62

ich in das gleiche Problem trat - wenn Sie Wenn Sie den FetchType.EAGER für ein @CollectionOfElements setzen, versucht der Hibernate, alles auf einmal zu erhalten, dh für jeden Eintrag eines Elements, das mit einem "Master" -Objekt verknüpft ist, eine einzige Abfrage zu verwenden. Dieses Problem kann erfolgreich zu Lasten der N + 1-Abfrage gelöst werden, wenn Sie die Anmerkung @Fetch (FetchMode.SELECT) zu Ihrer Sammlung hinzufügen. In meinem Fall wollte ich eine MediaObject-Entity mit einer Sammlung ihrer Metadatenelemente (Video-Codec, Audio-Codec, Größen usw.) haben.Die Zuordnung für eine metadataItems Sammlung sieht wie folgt aus:

 

@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

Vielen Dank dafür. – AHungerArtist

+1

Ich mochte die Lösung, weil wir das Ergebnis nicht mit einem Set umhüllen müssen, um Eindeutigkeit zu erhalten. – sanbhat

+0

Vielen Dank SOOOOO viel dafür !!! Rettete meinen Tag :) – pasql

5

ich dieses Problem konfrontiert, und ich löste es

mit

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Dies löscht die Duplikate aus, die durch die an die join gemacht verursacht werden Kindertabellen.

0

Anstelle von FetchMode.SELECT mit N + 1 Abfragen ist es besser, BatchSize e.q. @BatchSize(size = 200).

DISTINCT und Criteria.DISTINCT_ROOT_ENTITY hilft nicht, wenn Sie mehr als 1 Assoziation abrufen müssen. Für diesen Fall sieht andere Lösungen: https://stackoverflow.com/a/46013654/548473

Verwandte Themen