FetchMode Join vs-Anweisung ausgewertet
Ich habe zwei Tabellen Employee und Department folgenden sind die entity-Klassen für beide
Department.java
@Entity
@Table(name = "DEPARTMENT")
public class Department {
@Id
@Column(name = "DEPARTMENT_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer departmentId;
@Column(name = "DEPARTMENT_NAME")
private String departmentName;
@Column(name = "LOCATION")
private String location;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "department", orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
//@Fetch(FetchMode.JOIN)
private List<Employee> employees = new ArrayList<>();
}
Employee.java
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id
@SequenceGenerator(name = "emp_seq", sequenceName = "seq_employee")
@GeneratedValue(generator = "emp_seq")
@Column(name = "EMPLOYEE_ID")
private Integer employeeId;
@Column(name = "EMPLOYEE_NAME")
private String employeeName;
@ManyToOne
@JoinColumn(name = "DEPARTMENT_ID")
private Department department;
}
Unten sind die Abfragen, die ausgelöst wird, wenn ich habe em.find(Department.class, 1);
-- fetch mode = fetchmode.join
SELECT department0_.DEPARTMENT_ID AS DEPARTMENT_ID1_0_0_,
department0_.DEPARTMENT_NAME AS DEPARTMENT_NAME2_0_0_,
department0_.LOCATION AS LOCATION3_0_0_,
employees1_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_1_,
employees1_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_1_,
employees1_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_2_,
employees1_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_2_,
employees1_.EMPLOYEE_NAME AS EMPLOYEE_NAME2_1_2_
FROM DEPARTMENT department0_
LEFT OUTER JOIN EMPLOYEE employees1_
ON department0_.DEPARTMENT_ID =employees1_.DEPARTMENT_ID
WHERE department0_.DEPARTMENT_ID=?
-- fetch mode = fetchmode."Teilauswahl treffen"
SELECT department0_.DEPARTMENT_ID AS DEPARTMENT_ID1_0_0_,
department0_.DEPARTMENT_NAME AS DEPARTMENT_NAME2_0_0_,
department0_.LOCATION AS LOCATION3_0_0_
FROM DEPARTMENT department0_
WHERE department0_.DEPARTMENT_ID=?
SELECT employees0_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_0_,
employees0_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_0_,
employees0_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_1_,
employees0_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_1_,
employees0_.EMPLOYEE_NAME AS EMPLOYEE_NAME2_1_1_
FROM EMPLOYEE employees0_
WHERE employees0_.DEPARTMENT_ID=?
Ich wollte nur wissen, welche sollten wir lieber FetchMode.JOIN
oder FetchMode.SUBSELECT
? welches sollten wir uns entscheiden, in welchem Szenario?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Die UNTERABFRAGE Strategie, die Marmite bezieht, ist im Zusammenhang zu FetchMode.WÄHLEN Sie nicht "Teilauswahl treffen".
Die Ausgabe in der Konsole, die du gepostet hast über fetchmode."Teilauswahl treffen" ist merkwürdig, denn dies ist nicht die Art und Weise, dass funktionieren soll.
Den FetchMode."Teilauswahl treffen"
Hibernate docs:
FetchMode.Untergeordneten select-Anweisung sollte wie folgt Aussehen:
Können Sie sehen, dass diese zweite Abfrage bringen wird Speicher alle der Mitarbeiter, gehört zu einigen Departamentos (d.h. Mitarbeiter.department_id ist nicht null), ist es egal, wenn es nicht die Abteilung, die Sie abrufen, die in Ihrem ersten Abfrage.
Das ist also potentiell ein großes Problem, wenn die Tabelle der Mitarbeiter ist groß, weil es sein kann versehentlich das laden einer gesamten Datenbank in den Speicher.
Jedoch, FetchMode.UNTERGEORDNETE reduziert significatly die Anzahl der Abfragen, da braucht nur zwei Abfragen in comparisson auf die N+1 Abfragen der FecthMode.WÄHLEN Sie.
Denken Sie vielleicht, dass FetchMode.MITMACHEN macht noch weniger Abfragen, nur 1, warum also untergeordneten select-Anweisung überhaupt? Nun, es ist wahr, aber auf Kosten von duplizierten Daten und eine schwerere Reaktion.
Wenn eine einwertige proxy abgerufen werden, mit JOIN, die Abfrage abrufen:
Den Mitarbeiterdaten der boss ist dupliziert, wenn er leitet mehr als eine Abteilung und es sind Kosten in der Bandbreite.
Wenn einer faul-Sammlung abgerufen werden, mit JOIN, die Abfrage abrufen:
Die Abteilung Daten dupliziert werden, wenn es mit mehr als einem Mitarbeiter (der Natürliche Fall).
Wir nicht nur leiden, eine Kosten in der Bandbreite, aber auch wir bekommen doppelte dupliziert Abteilung Objekte und wir müssen einen SATZ oder DISTINCT_ROOT_ENTITY zu de-duplizieren.
Jedoch, doppelte Daten in pos, die eine niedrigere Latenz ist ein guter trade-off in vielen Fällen, wie Markus Winand sagt.
So, die wichtigste Frage über die Verwendung von untergeordneten select-Anweisung ist, dass ist schwer zu kontrollieren und beladen können ein ganzes Diagramm mit Entitäten in den Speicher.
Mit Batch-fetching Holen Sie die zugehörige Entität in einer separaten Abfrage, die als UNTERAUSWAHL (damit Sie nicht leiden Duplikate), nach und nach und die meisten wichtig, dass Sie die Abfrage nur mit Personen (damit Sie nicht leiden potenziell laden eine riesige Grafik), weil die IN der Unterabfrage wird gefiltert durch die IDs abgerufen, indem die outter Abfrage).
(Es kann interessant sein, zu testen, ob die Batch-fetching mit einem sehr hohen batch-Größe handeln würde, wie einer untergeordneten select-Anweisung, aber ohne das Problem der Last auf die gesamte Tabelle)
Ein paar Beiträge zeigen die verschiedenen bezaubernde Strategien und die SQL-logs (sehr wichtig):
Zusammenfassung:
Die Tabellen erstellt wurden, verwenden ascii-Tabellen.
Ich würde sagen, es kommt darauf an...
Let nehme an, Sie haben N Mitarbeiter in einer Abteilung, enthält D Byte Daten und einem durchschnittlichen Mitarbeiter aus E-bytes. (Bytes sind die Summe des Attributs Länge mit etwas overhead).
Mithilfe der join Strategie, die Sie durchführen 1 Abfrage und transfers N * (D + E) - Daten.
Mithilfe der Unterabfrage Strategie, die Sie durchführen 1 + N-Abfragen sind, sondern überträgt nur D + N*E-Daten.
In der Regel die N+1 Abfrage - ist die NO GO wenn N groß ist, so wird die VERKNÜPFUNG bevorzugt.
Aber eigentlich müssen Sie überprüfen Sie Ihre Laufleistung zwischen der Anzahl der Abfragen und die Daten übertragen.
Beachten Sie, dass ich nicht unter Berücksichtigung anderer Aspekte, wie die Hibernate-caching.
Zusätzliche subtile Aspekt könnte gültig sein, wenn die Mitarbeiter-Tabelle zu groß ist und Partitionierung - partition pruning auf den index-Zugriff kommt die überlegung, wie gut.
Einen Kunden (financial services) von mir hatte ein ähnliches problem, und er wollte "erwerben die Daten in eine einzelne Abfrage". Gut, ich erklärte, dass es besser ist, mehr als eine Abfrage, aufgrund der folgenden:
Für FetchMode.Sich dem Departement übertragen würden aus der Datenbank in die Anwendung einmal pro Mitarbeiter, da die join-operation resultiert in der Multiplikation der Abteilung pro Mitarbeiter. Wenn Sie 10 Abteilungen mit 100 Mitarbeitern jeder, jede von diesen 10 Abteilungen übertragen werden würde 100 mal innerhalb einer Abfrage, einfaches SQL. Also jede Abteilung, in diesem Fall übertragen wird 99-mal häufiger als nötig, was zu einer Daten-transfer-Aufwand für die Abteilung.
Für Fetchmode zwei untergeordneten select-Abfragen ausgelöst werden, in die Datenbank. Man würde verwendet werden, um die Daten von den 1000 Angestellten, um die 10 Abteilungen. Diese, für mich, klingt sehr viel effizienter. Für sicher, Sie würden sicherstellen, dass die Indizes sind im Ort, so dass die Daten abgerufen werden können, sofort.
Ich würde es vorziehen, FetchMode."Teilauswahl treffen".
Wäre es ein weiterer Fall, falls jede Abteilung hat nur einen Mitarbeiter, sondern, wie der name "Abteilung" suggeriert, wäre dies sehr unwahrscheinlich der Fall sein.
Schlage ich vor, die Messung der Zugriffszeiten auf die diese Theorie unterstützen. Für meine Kunden habe ich Messungen für verschiedene Arten von Zugriffen, und die "Abteilung" Tabelle für meine Kunden, hatte viele weitere Felder (ich habe nicht design, obwohl). So war es bald klar, dass die FetchMode.Teilauswahl treffen war viel schneller.
Planky sagte
Dies gilt aber nur, wenn es mehr als Department-Entität hidrated (was bedeutet, dass mehr als ein Mitarbeiter Sammlung nicht initialisierten), ich habe es getestet mit 3.6.10.Final und 4.3.8.Final
In Szenarien, In 2.2 (FetchMode.Untergeordneten select-hidrating 2 von 3 Abteilungen) und 3.2 (FetchMode.Untergeordneten select-hidrating alle Abteilungen), SubselectFetch.toSubselectString gibt die folgenden (die links zu den Hibernate-Klassen sind aus dem 4.3.8.End-tag):
Diese Unterabfrage wird nach dem erstellen der where-Klausel von OneToManyJoinWalker.initStatementString endend mit
Dann die where-Klausel Hinzugefügt wird, die in CollectionJoinWalker.whereString endend mit
In dieser Abfrage, in beiden Fällen sind alle Mitarbeiter abgerufen werden und hydratisiert.
Dies ist eindeutig ein Problem im Szenario 2.2, weil wir feuchtigkeitsspendende nur Abteilungen 1 und 2 aber auch Hydratisierung alle Mitarbeiter, auch wenn Sie nicht zu uns gehören zu diesen Dienststellen (in diesem Fall die Mitarbeiter der Abteilung 3).
Wenn es nur eine Abteilung Person hydratisiert in der Sitzung mit seinen Mitarbeitern Sammlung nicht initialisiert, dann die Abfrage wie eatSleepCode schrieb. Überprüfen Sie Szenario 1.2
Vom FetchStyle
Bis jetzt konnte ich nicht beheben, was das Javadoc bedeutet mit:UPDATE
Planky sagte:
Dies ist wahr und es ist ein sehr wichtiges detail, das ich getestet habe in der neuen Szenario 4.2
Die Abfrage generiert zu Holen Mitarbeiter ist
Die Unterabfrage in der where-Klausel enthält die ursprüngliche Beschränkung this_.department_name>=?, die Vermeidung der Belastung der Mitarbeiter.
Dies ist, was in der javadoc heißt mit
Alles, was ich gesagt habe über FetchMode.BEITRETEN und die Unterschiede mit FetchMode."Teilauswahl treffen" treu bleibt (und das gilt auch für FetchMode.WÄHLEN Sie).