17 Stimmen

Wie kann ich den Fremdschlüssel aus einer JPA ManyToOne-Zuordnung abrufen, ohne die Zieltabelle zu treffen?

Ich habe die folgenden zwei kommentierten Klassen, die ich zum Erstellen eines Diagramms verwende:

@Entity
@Table(name = "Edge")
public class Edge
{
    /* some code omitted for brevity */

    @ManyToOne
    @JoinColumn(name = "ixNodeFrom", nullable = false)
    private Node         _nodFrom;

    @ManyToOne
    @JoinColumn(name = "ixNodeTo", nullable = false)
    private Node         _nodTo;

    /* some code omitted for brevity */
}

@Entity
@Table(name = "Node")
public class Node
{
    /* some code omitted for brevity */

    @OneToMany(mappedBy = "_nodTo")
    private Set<Edge>    _rgInbound;

    @OneToMany(mappedBy = "_nodFrom")
    private Set<Edge>    _rgOutbound;

    /* some code omitted for brevity */
}

Wenn ich den Graphen aufbaue, gebe ich zwei Abfragen aus, um alle Zeilen aus einer der beiden Tabellen zu holen und die Kind-/Eltern-Referenzen einzurichten, für die ich die in der Edge Tisch.

Da ich die Beziehung zwischen den beiden Tabellen in JPA definiert habe, löst der Zugriff auf das Edge-Objekt zum Abrufen der IDs der beiden Knoten zwei SQL-Anweisungen pro Edge aus, wenn der JPA-Anbieter faul * lädt die zugehörigen Knotenpunkte. Da ich bereits über die Knotenobjekte verfüge und die IDs bereits aus der Kantentabelle geladen wurden, möchte ich diese Abfragen überspringen, da sie bei größeren Graphen furchtbar viel Zeit in Anspruch nehmen.

Ich habe versucht, diese Zeilen in die Edge Klasse, aber dann will mein JPA-Anbieter, dass ich eine Zuordnung schreibgeschützt mache, und ich kann nicht scheinen, einen Weg zu finden, wie man das macht:

@Column(name = "ixNodeTo")
private long _ixNodeTo;

@Column(name = "ixNodeFrom")
private long _ixNodeFrom;

Ich verwende Eclipselink und MySQL, falls das wichtig ist.


**Das Standardverhalten für @ManyToOne ist tatsächlich eifrig am Laden, siehe Pascals Antwort *

18voto

Hanno Fietz Punkte 29673

Ich habe drei gute Antworten erhalten, die gleichermaßen hilfreich waren, und inzwischen ist keine mehr durch die öffentliche Abstimmung an die Spitze gelangt, so dass ich sie hier zu einer einzigen umfassenden Antwort zusammenfasse:

a) Ändern Sie die Abfrage

Sie können den gesamten Graphen auf einmal laden, indem Sie die Abfrage ändern und so dem JPA-Anbieter die Möglichkeit geben, zu erkennen, dass er bereits alles im Speicher hat und nicht zur DB zurückkehren muss:

List<Node> nodes = em.createQuery(
        "SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
        .getResultList();

(über axtavt )

b) Verwenden Sie schreibgeschützte Felder für die FKs

Das Laden der FKs in ihre eigenen Felder, wie in der Frage beschrieben, funktioniert auch, wenn, wie vom JPA-Anbieter gefordert, die Felder als schreibgeschützt deklariert werden, was so gemacht wird:

@Column(name = "ixNodeTo", insertable = false, updatable = false)

(über bravocharlie )

c) Zugang zum Eigentum nutzen

Wenn Sie Folgendes verwenden Zugang zum Eigentum anstelle von Feldzugang Der JPA-Provider hat auch die Möglichkeit zu erkennen, dass er den FK bereits besitzt und das referenzierte Objekt nicht abrufen muss. Kurz gesagt, bedeutet Property Access, dass Sie die JPA-Annotationen auf den Getter setzen und damit dem JPA-Provider "versprechen", dass Ihr Getter nicht auf den Rest des Objekts zugreifen wird. Weitere Einzelheiten unter diese Frage . Dies funktioniert für Hibernate, und für Eclipselink funktioniert es (in der ursprünglichen Antwort angenommen, von mir experimentell bestätigt) mit aktiviertem Weaving. (über Pascal Thivent )


Zusätzlich, da Pascal weist in seiner Antwort darauf hin , @ManyToOne im Gegensatz zu meinem ursprünglichen Beitrag, ist nicht Lazy-Loading, sondern Eager-Loading standardmäßig, und ändern, dass Weben als auch erfordern.

14voto

bravocharlie Punkte 221

Haben Sie versucht

@Column(name = "ixNodeTo", insertable = false, updatable = false)

4voto

Pascal Thivent Punkte 548176

Wie kann ich den Fremdschlüssel aus einer JPA ManyToOne-Zuordnung abrufen, ohne die Zieltabelle zu treffen?

Theoretisch sollte ein JPA-Anbieter in der Lage sein, keine Abfrage auszulösen, wenn er

someEdge.getNodeFrom().getId()

da sie bereits die ID (als FK) hat.

Ich bin mir zu 100% sicher, dass Hibernate das kann (vorausgesetzt, Sie verwenden Zugang zum Eigentum ). Im Falle von EclipseLink weiß ich es nicht (wenn es das tut, wird es wahrscheinlich Weaving erfordern).

Da ich die Beziehung zwischen den beiden Tabellen in JPA definiert habe, löst der Zugriff auf das Edge-Objekt, um die IDs der beiden Knoten zu erhalten, zwei SQL-Anweisungen pro Edge aus, wenn der JPA-Anbieter die zugehörigen Knoten nachlädt. Da ich bereits über die Knotenobjekte verfüge und die IDs bereits aus der Kantentabelle geladen wurden, möchte ich diese Abfragen überspringen, da sie bei größeren Graphen sehr viel Zeit in Anspruch nehmen.

Beachten Sie, dass @ManyToOne verwendet eine EAGER Strategie standardmäßig. Wenn Sie es so wollen LAZY müssen Sie es explizit deklarieren (aber auch hier müssen Sie Ihre Klassen mit EclipseLink weben).

2voto

axtavt Punkte 233070

Ich denke, Sie sollten versuchen, Ihre Abfrage zu optimieren, anstatt die Zuordnung zu ändern. Die folgende Abfrage holt zum Beispiel den gesamten Graphen auf einmal (getestet in Hibernate):

List<Node> nodes = em.createQuery(
            "SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
            .getResultList();

0voto

Mike Argyriou Punkte 1180

Wie wäre es mit der Verwendung von getReference()?

Zum Beispiel:

Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()

[1] Dies löst keine SQL-Abfrage zum Abrufen der nodeFrom

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X