2010-09-03 18 views
10

Hat JPA 2 einen Mechanismus zum Ausführen rekursiver Abfragen?Rekursive JPA-Abfrage?

Hier ist meine Situation: Ich habe eine Entität E, die ein ganzzahliges Feld x enthält. Es kann auch Kinder vom Typ E haben, die über @OneToMany zugeordnet sind. Was ich tun möchte, ist ein E nach Primärschlüssel zu finden und den Wert von x zusammen mit den x-Werten aller seiner Nachkommen zu erhalten. Gibt es eine Möglichkeit, dies in einer einzigen Abfrage zu tun?

Ich verwende Hibernate 3.5.3, aber ich möchte keine expliziten Abhängigkeiten von Hibernate-APIs haben.


EDIT: Nach this Artikel, tut Hibernate nicht diese Funktion haben, oder zumindest tat es nicht im März. Es scheint also unwahrscheinlich, dass JPA es haben würde, aber ich möchte sichergehen.

Antwort

21

Verwendung des einfachen Adjazenzmodells wo jede Zeile einen Verweis auf ihre Eltern enthält, die sich auf eine andere Zeile in der gleichen Tabelle beziehen, funktioniert nicht gut mit JPA. Dies liegt daran, dass JPA keine Unterstützung für das Generieren von Abfragen unter Verwendung der Oracle CONNECT BY-Klausel oder der SQL-Standard-WITH-Anweisung bietet. Ohne eine dieser beiden Klauseln ist es nicht wirklich möglich, das Adjazenzmodell nützlich zu machen.

Es gibt jedoch ein paar andere Ansätze zur Modellierung dieses Problems, die auf dieses Problem angewendet werden können. Das erste ist das Materialized Path Model. Hier wird der vollständige Pfad zum Knoten in eine einzelne Spalte abgeflacht. Die Tabellendefinition ist wie so erweitert:

CREATE TABLE node (id INTEGER, 
        path VARCHAR, 
        parent_id INTEGER REFERENCES node(id)); 

einen Baum von Knoten einzufügen sieht etwas, was wie:

INSERT INTO node VALUES (1, '1', NULL); -- Root Node 
INSERT INTO node VALUES (2, '1.2', 1); -- 1st Child of '1' 
INSERT INTO node VALUES (3, '1.3', 1); -- 2nd Child of '1' 
INSERT INTO node VALUES (4, '1.3.4', 3); -- Child of '3' 

So Knoten ‚1‘ und alle seine Kinder bekommen die Abfrage:

Um dies JPA zuzuordnen, machen Sie einfach die 'Pfad' Spalte ein Attribut Ihres persistenten Objekts. Sie müssen jedoch die Buchhaltung führen, um das Feld "Pfad" auf dem neuesten Stand zu halten. JPA/Hibernate wird dies nicht für Sie tun. Z.B. Wenn Sie den Knoten auf ein anderes Elternelement verschieben, müssen Sie sowohl den übergeordneten Verweis aktualisieren als auch den neuen Pfadwert aus dem neuen übergeordneten Objekt ermitteln.

Der andere Ansatz heißt verschachtelte Set-Modell, die etwas komplexer ist. Wahrscheinlich am besten described von seinem Urheber (anstatt von mir wortwörtlich hinzugefügt).

Es gibt einen dritten Ansatz, der als verschachteltes Intervallmodell bezeichnet wird, das sich jedoch stark auf gespeicherte Prozeduren stützt, die implementiert werden müssen.

Eine ausführlichere Erklärung dieses Problems ist in Kapitel 7 von The Art of SQL beschrieben.

+0

Das ist alles viel komplexer als das, was ich suchte, aber danke für die informative Antwort. Es gibt auch eine einfachere Alternative zum materialisierten Pfadmodell - jeder Knoten speichert die ID des Wurzelknotens des Baumes, zu dem er gehört. Das bedeutet, dass Sie nur den Wurzelknoten als Startpunkt der Abfrage verwenden können, aber in meinem speziellen Fall ist das kein Problem - das werde ich wahrscheinlich tun. –

+0

Sie können Adjazenzlisten verwenden, die über JPA verwaltet werden, aber eine systemeigene Abfrage verwenden, um rekursive SQL-Funktionen zu verwenden. Es wäre ein bisschen eklig und nicht tragbar, aber es wäre etwas von einem Besten beider Welten. Verschachtelte Sätze sind zwar portabel, aber knifflig und eignen sich für einige ziemlich häufige Arten von Abfragen nicht besonders gut. –

+0

Hat sich mit JPA 2.0 etwas geändert ... Zum Beispiel habe ich eine Native NamedQuery verwendet, um in meiner Abfrage eine CONNECT BY-Klausel zu haben. Ich habe es versucht und es gibt die Kinder zurück, aber nicht in einer hierarchischen Liste Form ... Nur flache Kinder. Irgendwelche Ideen? – SoftwareSavant

4

Die beste Antwort in diesem Post scheint wie ein massiver Work-Around-Hack für mich. Ich musste mich bereits mit Datenmodellen auseinandersetzen, bei denen brillante Ingenieure entschieden, dass es eine gute Idee wäre, Tree Hiarchies in DB-Feldern wie "Europa | Uk | Shop1 | John" und mit riesigen Datenmengen in diesen Tabellen zu codieren . Nicht überraschend, die Leistung der Abfrage der Form MyHackedTreeField LIKE 'parentHierharchy%' wo Killer. Adressierung dieser Art von Problem schließlich erforderlich Erstellen von In-Memory-Cache der Baum-Hierarchie und so viele andere ...

Wenn Sie eine rekursive Abfrage ausführen müssen und Ihr Datenvolumen nicht massiv ist ... machen Sie Ihr Leben einfach und laden Sie einfach die DB-Felder, die Sie zum Ausführen Ihres Plans benötigen. Und kodiere deine Rekursion in Java. Mache es nicht in der Datenbank, außer du hast einen guten Grund, es zu tun.

Und selbst wenn die Menge der Daten, die Sie haben, massiv ist, können Sie Ihr Problem höchstwahrscheinlich in unabhängige rekursive Baumstapel unterteilen und diese gleichzeitig verarbeiten, ohne dass alle Daten gleichzeitig geladen werden müssen.