In Anbetracht des folgenden Domänenmodells möchte ich alle Answer
s einschließlich ihrer Value
s und ihrer jeweiligen Unterkinder laden und in eine AnswerDTO
ablegen, um sie dann in JSON zu konvertieren. Ich habe eine funktionierende Lösung, aber sie leidet unter dem N + 1-Problem, das ich mithilfe eines Ad-hoc-Problems beseitigen möchte @EntityGraph
. Alle Zuordnungen sind konfiguriert LAZY
.
@Query("SELECT a FROM Answer a")
@EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Mit einem Ad-hoc- Verfahren @EntityGraph
für die Repository
Methode kann ich sicherstellen, dass die Werte vorab abgerufen werden, um N + 1 für die Answer->Value
Zuordnung zu verhindern . Während mein Ergebnis in Ordnung ist, gibt es ein weiteres N + 1-Problem, da die selected
Assoziation der MCValue
s verzögert geladen wird .
Verwenden Sie dies
@EntityGraph(attributePaths = {"value.selected"})
schlägt fehl, weil das selected
Feld natürlich nur ein Teil einiger Value
Entitäten ist:
Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Value];
Wie kann ich JPA mitteilen, dass nur dann versucht wird, die selected
Zuordnung abzurufen, wenn der Wert a ist MCValue
? Ich brauche so etwas optionalAttributePaths
.
quelle
selected
von Antworten mit aMCValue
. Ich mochte es nicht, dass dies eine zusätzliche Schleife erfordern würde und ich die Zuordnung zwischen den Datensätzen verwalten müsste. Ich mag Ihre Idee, den Hibernate-Cache dafür auszunutzen. Können Sie erläutern, wie sicher (in Bezug auf die Konsistenz) es ist, sich auf den Cache zu verlassen, um die Ergebnisse zu enthalten? Funktioniert dies, wenn die Abfragen in einer Transaktion durchgeführt werden? Ich habe Angst vor schwer zu erkennenden und sporadisch faulen Initialisierungsfehlern.MCValue
Entitäten. Und Sie brauchen keine zusätzliche Schleife. Sie sollten alleMCValue
Entitäten mit einer Abfrage abrufen, dieAnswer
mit der WHERE-Klausel verknüpft ist und dieselbe WHERE-Klausel wie Ihre aktuelle Abfrage verwendet. Ich habe auch im heutigen Live-Stream darüber gesprochen: youtu.be/70B9znTmi00?t=238 Es begann um 3:58 Uhr, aber ich habe zwischendurch ein paar andere Fragen gestellt ...SINGLE_TABLE_INHERITANCE
.Ich weiß nicht, was Spring-Data dort tut, aber um dies zu tun, müssen Sie normalerweise den
TREAT
Operator verwenden, um auf die Unterzuordnung zugreifen zu können, aber die Implementierung für diesen Operator ist ziemlich fehlerhaft. Hibernate unterstützt den impliziten Zugriff auf Subtyp-Eigenschaften, den Sie hier benötigen würden, aber anscheinend kann Spring-Data dies nicht richtig handhaben. Ich kann Ihnen empfehlen, sich Blaze-Persistence Entity-Views anzusehen , eine Bibliothek, die auf JPA aufbaut und es Ihnen ermöglicht, beliebige Strukturen Ihrem Entitätsmodell zuzuordnen. Sie können Ihr DTO-Modell typsicher zuordnen, auch die Vererbungsstruktur. Entitätsansichten für Ihren Anwendungsfall könnten folgendermaßen aussehenMit der von Blaze-Persistence bereitgestellten Spring-Datenintegration können Sie ein solches Repository definieren und das Ergebnis direkt verwenden
Es wird eine HQL-Abfrage generiert, die genau das auswählt, was Sie in
AnswerDTO
der folgenden Abbildung zugeordnet haben.quelle
interface MCValueDTO extends ValueDTO { @Mapping("selected.id") Set<Long> getOption(); }
Mein letztes Projekt verwendete GraphQL (eine Premiere für mich) und wir hatten ein großes Problem mit N + 1-Abfragen und versuchten, die Abfragen so zu optimieren, dass sie nur dann für Tabellen verknüpft werden, wenn sie benötigt werden. Ich habe festgestellt, dass Cosium / spring-data-jpa-entity-graph unersetzlich ist. Es erweitert
JpaRepository
und fügt Methoden zum Übergeben eines Entitätsdiagramms an die Abfrage hinzu. Anschließend können Sie zur Laufzeit dynamische Entitätsdiagramme erstellen, um nur für die benötigten Daten Linksverknüpfungen hinzuzufügen.Unser Datenfluss sieht ungefähr so aus:
Um das Problem zu lösen, dass ungültige Knoten nicht in das Entitätsdiagramm aufgenommen werden (z. B.
__typename
aus graphql), habe ich eine Dienstprogrammklasse erstellt, die die Generierung des Entitätsdiagramms übernimmt. Die aufrufende Klasse übergibt den Klassennamen, für den sie das Diagramm generiert, und validiert dann jeden Knoten im Diagramm anhand des vom ORM verwalteten Metamodells. Wenn sich der Knoten nicht im Modell befindet, wird er aus der Liste der Diagrammknoten entfernt. (Diese Prüfung muss rekursiv sein und auch jedes Kind prüfen.)Bevor ich dies fand, hatte ich Projektionen und jede andere in den Spring JPA / Hibernate-Dokumenten empfohlene Alternative ausprobiert, aber nichts schien das Problem elegant oder zumindest mit einer Menge zusätzlichen Codes zu lösen
quelle
selected
Zuordnung nicht für alle Untertypen von verfügbar istvalue
.Nach Ihrem Kommentar bearbeitet:
Ich entschuldige mich, ich habe Ihr Problem in der ersten Runde nicht verstanden. Ihr Problem tritt beim Start von Spring-Daten auf, nicht nur, wenn Sie versuchen, findAll () aufzurufen.
So können Sie jetzt navigieren. Das vollständige Beispiel kann von meinem Github abgerufen werden: https://github.com/bdzzaid/stackoverflow-java/blob/master/jpa-hibernate/
Sie können Ihr Problem in diesem Projekt einfach reproduzieren und beheben.
Tatsächlich können Spring-Daten und der Ruhezustand das "ausgewählte" Diagramm standardmäßig nicht bestimmen, und Sie müssen angeben, wie die ausgewählte Option erfasst werden soll.
Sie müssen also zuerst die NamedEntityGraphs der Klasse Answer deklarieren
Wie Sie sehen können, gibt es zwei NamedEntityGraph für das Attribut - Wert der Klasse Antwort
Der erste für alle Wert ohne spezifische Beziehung zur Last
Die zweite für den spezifischen Multichoice- Wert. Wenn Sie diese entfernen, reproduzieren Sie die Ausnahme.
Zweitens müssen Sie sich in einem Transaktionskontext antworten. AnswerRepository.findAll (), wenn Sie Daten vom Typ LAZY abrufen möchten
quelle
value
von abzurufen,Answer
sondern dieselected
Assoziation zu erhalten, fallsvalue
es sich um eine handeltMCValue
. Ihre Antwort enthält keine diesbezüglichen Informationen.OneToMany
so definiert habenFetchType.EAGER
, wie in der Frage angegeben: Alle Zuordnungen sindLAZY
.selected
für jede Antwort eine DB-Abfrage ausgibt , anstatt sie im Voraus zu laden.