Was genau steuert die Abrufstrategie von JPA? Ich kann keinen Unterschied zwischen eifrig und faul feststellen. In beiden Fällen verbindet JPA / Hibernate nicht automatisch viele-zu-eins-Beziehungen.
Beispiel: Person hat eine einzelne Adresse. Eine Adresse kann vielen Personen gehören. Die mit JPA-Annotationen versehenen Entitätsklassen sehen folgendermaßen aus:
@Entity
public class Person {
@Id
public Integer id;
public String name;
@ManyToOne(fetch=FetchType.LAZY or EAGER)
public Address address;
}
@Entity
public class Address {
@Id
public Integer id;
public String name;
}
Wenn ich die JPA-Abfrage verwende:
select p from Person p where ...
JPA / Hibernate generiert eine SQL-Abfrage zur Auswahl aus der Personentabelle und anschließend eine eigene Adressabfrage für jede Person:
select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3
Dies ist sehr schlecht für große Ergebnismengen. Bei 1000 Personen werden 1001 Abfragen generiert (1 von Person und 1000 von Adresse). Ich weiß das, weil ich mir das Abfrageprotokoll von MySQL ansehe. Nach meinem Verständnis führt das Festlegen des Abruftyps der Adresse auf eifrig dazu, dass JPA / Hibernate automatisch mit einem Join abfragt. Unabhängig vom Abruftyp werden jedoch immer noch unterschiedliche Abfragen für Beziehungen generiert.
Nur wenn ich ihm ausdrücklich sage, dass er beitreten soll, tritt er tatsächlich bei:
select p, a from Person p left join p.address a where ...
Vermisse ich hier etwas? Ich muss jetzt jede Abfrage von Hand codieren, damit sie die vielen-zu-eins-Beziehungen verbindet. Ich verwende die JPA-Implementierung von Hibernate mit MySQL.
Bearbeiten: Es scheint (siehe FAQ zum Ruhezustand hier und hier ), dass FetchType
JPA-Abfragen keine Auswirkungen haben. In meinem Fall habe ich ihm ausdrücklich gesagt, dass er beitreten soll.
Antworten:
JPA bietet keine Spezifikation für die Zuordnung von Anmerkungen zur Auswahl der Abrufstrategie. Im Allgemeinen können verwandte Entitäten auf eine der folgenden Arten abgerufen werden
Also
SELECT
undJOIN
sind zwei Extreme undSUBSELECT
fällt dazwischen. Man kann eine geeignete Strategie basierend auf seinem Domain-Modell wählen.Standardmäßig
SELECT
wird sowohl von JPA / EclipseLink als auch von Hibernate verwendet. Dies kann überschrieben werden mit:im Ruhezustand. Es ermöglicht auch die
SELECT
explizite Einstellung des Modus,@Fetch(FetchMode.SELECT)
der mithilfe der Stapelgröße eingestellt werden kann, z@BatchSize(size=10)
.Entsprechende Anmerkungen in EclipseLink sind:
quelle
"mxc" ist richtig.
fetchType
Gibt nur an, wann die Beziehung aufgelöst werden soll.Um das eifrige Laden mithilfe eines äußeren Joins zu optimieren, müssen Sie hinzufügen
zu Ihrem Feld. Dies ist eine Annotation im Ruhezustand.
quelle
Das Attribut fetchType steuert, ob das mit Anmerkungen versehene Feld sofort abgerufen wird, wenn die primäre Entität abgerufen wird. Es gibt nicht unbedingt vor, wie die Abrufanweisung aufgebaut ist. Die tatsächliche SQL-Implementierung hängt von dem Anbieter ab, den Sie für den Link / Ruhezustand usw. verwenden.
Wenn Sie festlegen
fetchType=EAGER
Dies bedeutet, dass das mit Anmerkungen versehene Feld gleichzeitig mit den anderen Feldern in der Entität mit seinen Werten gefüllt wird. Wenn Sie also einen Entitymanager öffnen, rufen Sie Ihre Personenobjekte ab und schließen Sie den Entitymanager. Wenn Sie anschließend eine person.address ausführen, wird keine Ausnahme für das verzögerte Laden ausgelöst.Wenn Sie festlegen, wird
fetchType=LAZY
das Feld nur beim Zugriff ausgefüllt. Wenn Sie den Entitymanager bis dahin geschlossen haben, wird eine verzögerte Ladeausnahme ausgelöst, wenn Sie eine person.address ausführen. Um das Feld zu laden, müssen Sie die Entität mit em.merge () wieder in einen EntityManger-Kontext stellen, dann den Feldzugriff durchführen und dann den Entitymanager schließen.Möglicherweise möchten Sie beim Erstellen einer Kundenklasse mit einer Sammlung für Kundenaufträge verzögert laden. Wenn Sie jede Bestellung für einen Kunden abgerufen haben, als Sie eine Kundenliste erhalten wollten, kann dies eine teure Datenbankoperation sein, wenn Sie nur nach Kundennamen und Kontaktdaten suchen. Am besten lassen Sie den Datenbankzugang bis später.
Für den zweiten Teil der Frage: Wie kann man den Ruhezustand aktivieren, um optimiertes SQL zu generieren?
Im Ruhezustand sollten Sie Hinweise zum Erstellen der effizientesten Abfrage geben können, aber ich vermute, dass mit Ihrer Tabellenkonstruktion etwas nicht stimmt. Ist die Beziehung in den Tabellen festgelegt? Der Ruhezustand hat möglicherweise entschieden, dass eine einfache Abfrage schneller als ein Join ist, insbesondere wenn Indizes usw. fehlen.
quelle
Versuche es mit:
Es funktioniert bei mir ähnlich wie bei JPA2 / EclipseLink, aber es scheint, dass diese Funktion auch in JPA1 vorhanden ist :
quelle
Wenn Sie EclipseLink anstelle von Hibernate verwenden, können Sie Ihre Abfragen durch "Abfragehinweise" optimieren. Siehe diesen Artikel aus dem Eclipse-Wiki: EclipseLink / Examples / JPA / QueryOptimization .
Es gibt ein Kapitel über "Joined Reading".
quelle
Um Mitglied zu werden, können Sie mehrere Dinge tun (mit eclipselink)
in jpql können sie left join abrufen
In der benannten Abfrage können Sie einen Abfragehinweis angeben
In TypedQuery können Sie so etwas sagen
query.setHint("eclipselink.join-fetch", "e.projects.milestones");
Es gibt auch einen Batch-Abruf-Hinweis
query.setHint("eclipselink.batch", "e.address");
sehen
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
quelle
Ich hatte genau dieses Problem mit der Ausnahme, dass die Person-Klasse eine eingebettete Schlüsselklasse hatte. Meine eigene Lösung bestand darin, sie in die Abfrage einzubeziehen UND zu entfernen
@Fetch(FetchMode.JOIN)
Meine eingebettete ID-Klasse:
quelle
Mir fallen zwei Dinge ein.
Sind Sie sicher, dass Sie ManyToOne als Adresse meinen? Das bedeutet, dass mehrere Personen dieselbe Adresse haben. Wenn es für einen von ihnen bearbeitet wird, wird es für alle bearbeitet. Ist das deine Absicht? 99% der Zeitadressen sind "privat" (in dem Sinne, dass sie nur einer Person gehören).
Zweitens, haben Sie andere eifrige Beziehungen zur Entität Person? Wenn ich mich richtig erinnere, kann Hibernate nur eine eifrige Beziehung zu einer Entität verarbeiten, aber das sind möglicherweise veraltete Informationen.
Ich sage das, weil Ihr Verständnis, wie dies funktionieren sollte, von meinem Sitzplatz aus im Wesentlichen richtig ist.
quelle