Was ist der Unterschied zwischen unidirektionalen und bidirektionalen JPA- und Hibernate-Assoziationen?

135

Was ist der Unterschied zwischen unidirektionalen und bidirektionalen Assoziationen?

Da die in der Datenbank generierte Tabelle alle gleich ist, besteht der einzige Unterschied darin, dass sich jede Seite der bidirektionalen Zuordnungen auf die andere bezieht und die unidirektionale nicht.

Dies ist eine unidirektionale Zuordnung

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

Die bidirektionale Assoziation

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

Der Unterschied besteht darin, ob die Gruppe eine Referenz des Benutzers enthält.

Ich frage mich also, ob dies der einzige Unterschied ist. was wird empfohlen?

hguser
quelle
7
Die Gruppe weiß nun, welche Benutzer sie enthält. Ich denke nicht, dass dies ein kleiner Unterschied ist.
Satadru Biswas
5
Bidirektionale Beziehungen wurden für mich zu einem Chaos, wenn es um Aktualisierungen geht. :)
diyoda_

Antworten:

152

Der Hauptunterschied besteht darin, dass die bidirektionale Beziehung einen Navigationszugriff in beide Richtungen bietet, sodass Sie ohne explizite Abfragen auf die andere Seite zugreifen können. Außerdem können Sie Kaskadenoptionen auf beide Richtungen anwenden.

Beachten Sie, dass der Navigationszugriff nicht immer gut ist, insbesondere für "Eins-zu-Sehr-Viele" - und "Viele-zu-Sehr-Viele" -Beziehungen. Stellen Sie sich eine vor Group, die Tausende von Users enthält :

  • Wie würden Sie darauf zugreifen? Bei so vielen Users müssen Sie normalerweise eine Filterung und / oder Paginierung anwenden, damit Sie trotzdem eine Abfrage ausführen müssen (es sei denn, Sie verwenden eine Sammlungsfilterung , die für mich wie ein Hack aussieht). Einige Entwickler neigen in solchen Fällen dazu, Filterung im Speicher anzuwenden, was offensichtlich nicht gut für die Leistung ist. Beachten Sie, dass eine solche Beziehung diese Art von Entwicklern dazu ermutigen kann, sie zu verwenden, ohne die Auswirkungen auf die Leistung zu berücksichtigen.

  • Wie würden Sie dem neue Users hinzufügen Group? Glücklicherweise betrachtet Hibernate die eigene Seite der Beziehung, wenn sie beibehalten wird, sodass Sie nur festlegen können User.group. Wenn Sie jedoch wollen Objekte im Speicher konsistent zu halten, müssen Sie auch hinzufügen Userzu Group.users. Aber es würde Hibernate dazu bringen, alle Elemente Group.usersaus der Datenbank abzurufen !

Daher kann ich der Empfehlung der Best Practices nicht zustimmen . Sie müssen bidirektionale Beziehungen sorgfältig entwerfen und dabei Anwendungsfälle (benötigen Sie einen Navigationszugriff in beide Richtungen?) Und mögliche Auswirkungen auf die Leistung berücksichtigen.

Siehe auch:

axtavt
quelle
Hallo, danke, Sie scheinen ein Experte für den Winterschlaf zu sein, da Sie meine Frage beantwortet haben: stackoverflow.com/questions/5350770/… . Und jetzt bin ich mir nicht sicher, ob die Beziehung hält, da ich nicht mehr in den Kommentar schreiben kann, also poste ich es hier dpaste.de/J85m. Bitte überprüfen Sie es, wenn möglich. :)
hguser
@hguser: Wenn Sie schließlich entscheiden Sie Beziehung bidirektionale zu machen, denke ich , es besser zu Anruf wäre setGroup()aus , addUser()um beiden Seiten in Einklang zu halten.
Axtavt
Wie wäre es, wenn ich setGroup () nicht in group.addUser () aufrufe?
Hguser
@hguser: Die Beziehung würde nicht bestehen bleiben, siehe Punkt 2 in der Antwort. Sie können setGroup()ohne aufrufen addUser(), dies würde jedoch zu einem inkonsistenten Status der Objekte im Speicher führen.
Axtavt
Ich habe festgestellt, dass ich keine korrekte Zuordnung vornehmen kann. Können Sie sich etwas Zeit nehmen, um mein Projekt bei github zu überprüfen? Es ist ein kleines Projekt.
hguser
30

Es gibt zwei Hauptunterschiede.

Zugriff auf die Assoziationsseiten

Der erste bezieht sich darauf, wie Sie auf die Beziehung zugreifen. Bei einer unidirektionalen Zuordnung können Sie die Zuordnung nur von einem Ende aus navigieren.

Für eine unidirektionale @ManyToOneZuordnung bedeutet dies, dass Sie nur von der untergeordneten Seite auf die Beziehung zugreifen können, auf der sich der Fremdschlüssel befindet.

Wenn Sie eine unidirektionale @OneToManyZuordnung haben, bedeutet dies, dass Sie nur von der übergeordneten Seite auf die Beziehung zugreifen können, auf der sich der Fremdschlüssel befindet.

Für die bidirektionale @OneToManyZuordnung können Sie in der Zuordnung auf beide Arten navigieren, entweder von der übergeordneten oder von der untergeordneten Seite.

Sie müssen auch Dienstprogrammmethoden zum Hinzufügen / Entfernen für bidirektionale Zuordnungen verwenden, um sicherzustellen, dass beide Seiten ordnungsgemäß synchronisiert sind .

Performance

Der zweite Aspekt bezieht sich auf die Leistung.

  1. Für @OneToMany, unidirektionale Assoziationen nicht ausführen sowie bidirektionale diejenigen .
  2. Für @OneToOne, führt dazu , dass eine bidirektionale Vereinigung der Eltern geholt eifrig sein , wenn Hibernate nicht , ob der Proxy sollte oder ein Nullwert zugewiesen werden kann sagen .
  3. Für @ManyToMany, macht die Sammlung Art schon ein Unterschied , wie Setseine bessere Leistung alsLists .
Vlad Mihalcea
quelle
11

In Bezug auf die Codierung ist die Implementierung einer bidirektionalen Beziehung komplexer, da die Anwendung dafür verantwortlich ist, beide Seiten gemäß JPA-Spezifikation 5 (auf Seite 42) synchron zu halten. Leider enthält das in der Spezifikation angegebene Beispiel keine näheren Angaben, sodass es keine Vorstellung vom Grad der Komplexität gibt.

Wenn Sie keinen Cache der zweiten Ebene verwenden, ist es normalerweise kein Problem, die Beziehungsmethoden nicht korrekt implementiert zu haben, da die Instanzen am Ende der Transaktion verworfen werden.

Wenn bei Verwendung des Cache der zweiten Ebene etwas aufgrund falsch implementierter Methoden zur Behandlung von Beziehungen beschädigt wird, bedeutet dies, dass auch bei anderen Transaktionen die beschädigten Elemente angezeigt werden (der Cache der zweiten Ebene ist global).

Eine korrekt implementierte bidirektionale Beziehung kann Abfragen und den Code vereinfachen, sollte jedoch nicht verwendet werden, wenn dies in Bezug auf die Geschäftslogik nicht wirklich sinnvoll ist.

Constantino Cronemberger
quelle
9

Ich bin nicht 100% sicher, dass dies der einzige Unterschied ist, aber es ist der Hauptunterschied . Es wird auch empfohlen, bidirektionale Assoziationen durch die Hibernate-Dokumente zu haben:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

Speziell:

Bidirektionale Assoziationen bevorzugen: Unidirektionale Assoziationen sind schwieriger abzufragen. In einer großen Anwendung müssen fast alle Zuordnungen in Abfragen in beide Richtungen navigierbar sein.

Ich persönlich habe ein kleines Problem mit dieser Decke Empfehlung - scheint es mir , gibt es Fälle , in denen ein Kind keinen praktischen Grund hat über seine Eltern kennen (zB warum eine Auftragsposition Bedarf über den Auftrag wissen , es ist verbunden mit?), aber ich sehe auch einen angemessenen Teil der Zeit darin Wert. Und da die Bidirektionalität nichts wirklich schadet, finde ich es nicht zu verwerflich, daran festzuhalten.

kvista
quelle
Die Bidirektionalität kann in einigen Fällen schaden, wie axtavt erklärt hat! Ich wäre sehr vorsichtig mit jeder Beziehung, die in einer Hibernate-Entität abgebildet wird! Sie können endlose Zeit zum Laden von Dingen in den Ruhezustand benötigen, es sei denn, Sie wissen genau, was Sie tun. Denken Sie sorgfältig über die Anwendungsfälle nach und verwenden Sie unterschiedliche Modellklassen für unterschiedliche Anwendungsfälle. F.ex. In einer Liste von Dingen benötige ich nicht alle verwandten Objekte, nur eine Beschriftung und die ID. Die Listenentität ist für mich also eine andere (und sehr einfache) als die Detailentität.
Cslotty