Viele-zu-Viele-Beziehungen in JPA

Many-to-Many-Beziehung in JPA

1. Einführung

In diesem Tutorial sehen wir mehrere Möglichkeiten fürdeal with many-to-many relationships using JPA.

Um die Ideen zu präsentieren, verwenden wir ein Modell von Studenten, Kursen und verschiedenen Beziehungen zwischen ihnen.

Der Einfachheit halber werden in den Codebeispielen nur die Attribute und die JPA-Konfiguration angezeigt, die sich auf die Viele-zu-Viele-Beziehungen beziehen.

Weitere Lektüre:

Zuordnen von Entitätsklassennamen zu SQL-Tabellennamen mit JPA

Erfahren Sie, wie Tabellennamen standardmäßig generiert werden und wie Sie dieses Verhalten überschreiben.

Read more

Übersicht über JPA / Hibernate-Kaskadentypen

Ein schneller und praktischer Überblick über JPA / Hibernate Cascade Types.

Read more

2. Grundlegende Many-to-Many

2.1. Modellierung einer Many-to-Many-Beziehung

Eine Beziehung ist eine Verbindung zwischen zwei Arten von Entitäten. Im Falle einer Viele-zu-Viele-Beziehung können sich beide Seiten auf mehrere Instanzen der anderen Seite beziehen.

Beachten Sie, dass Entitätstypen möglicherweise in einer Beziehung zu sich selbst stehen. Wenn wir beispielsweise Stammbäume modellieren: Jeder Knoten ist eine Person. Wenn wir also über die Eltern-Kind-Beziehung sprechen, sind beide Teilnehmer eine Person.

Es spielt jedoch keine Rolle, ob es sich um eine Beziehung zwischen einzelnen oder mehreren Entitätstypen handelt. Da es einfacher ist, über Beziehungen zwischen zwei verschiedenen Entitätstypen nachzudenken, werden wir dies verwenden, um unsere Fälle zu veranschaulichen.

Wenn die Schüler beispielsweise die Kurse markieren, die ihnen gefallen: Ein Schüler kannmanyKurse mögen, undmanychüler können denselben Kurs mögen:

image

Wie wir wissen, können wir in RDBMS Beziehungen zu Fremdschlüsseln erstellen. Da beide Seiten in der Lage sein sollten, auf die andere zu verweisen, giltwe need to create a separate table to hold the foreign keys:

image

Eine solche Tabelle heißtjoin table. Beachten Sie, dass in einer Join-Tabelle die Kombination der Fremdschlüssel der zusammengesetzte Primärschlüssel ist.

2.2. Implementierung in JPA

Modeling a many-to-many relationship with POJOs ist einfach. Wir sollteninclude a Collection in both classes, das die Elemente der anderen enthält.

Danach müssen wir die Klasse mit@Entity und den Primärschlüssel mit@Id markieren, damit sie zu richtigen JPA-Entitäten werden.

Außerdem sollten wir den Beziehungstyp konfigurieren. Daherwe mark the collections with @ManyToMany Anmerkungen:

@Entity
class Student {

    @Id
    Long id;

    @ManyToMany
    Set likedCourses;

    // additional properties
    // standard constructors, getters, and setters
}

@Entity
class Course {

    @Id
    Long id;

    @ManyToMany
    Set likes;

    // additional properties
    // standard constructors, getters, and setters
}

Außerdem müssen wir konfigurieren, wie die Beziehung im RDBMS modelliert werden soll.

Auf der Besitzerseite konfigurieren wir die Beziehung. In diesem Beispiel wählen wir die KlasseStudentaus.

We can do this with the @JoinTable annotation in the Student class. Wir geben den Namen der Verknüpfungstabelle (course_like) und die Fremdschlüssel mit den Anmerkungen@JoinColumn an. Das AttributjoinColumn verbindet sich mit der Eigentümerseite der Beziehung und das AttributinverseJoinColumn mit der anderen Seite:

@ManyToMany
@JoinTable(
  name = "course_like",
  joinColumns = @JoinColumn(name = "student_id"),
  inverseJoinColumns = @JoinColumn(name = "course_id"))
Set likedCourses;

Beachten Sie, dass die Verwendung von@JoinTable oder sogar@JoinColumn nicht erforderlich ist: JPA generiert die Tabellen- und Spaltennamen für uns. Die von JPA verwendete Strategie entspricht jedoch nicht immer den von uns verwendeten Namenskonventionen. Daher die Möglichkeit, Tabellen- und Spaltennamen zu konfigurieren.

On the target side, we only have to provide the name of the field, which maps the relationship. Daher setzen wir das AttributmappedBy der Annotation@ManyToMany in der KlasseCourse:

@ManyToMany(mappedBy = "likedCourses")
Set likes;

Beachten Sie, dass wir seita many-to-many relationship doesn’t have an owner side in the database die Join-Tabelle in der KlasseCoursekonfigurieren und aus der KlasseStudentreferenzieren können.

3. Viele-zu-Viele-Verwendung eines zusammengesetzten Schlüssels

3.1. Modellierung von Beziehungsattributen

Nehmen wir an, wir möchten, dass die Schüler die Kurse bewerten. Ein Student kann eine beliebige Anzahl von Kursen bewerten, und eine beliebige Anzahl von Studenten kann den gleichen Kurs bewerten. Daher ist es auch eine Viele-zu-Viele-Beziehung. Was es etwas komplizierter macht, ist, dassthere is more to the rating relationship than the fact that it exists. We need to store the rating score the student gave on the course.

Wo können wir diese Informationen speichern? Wir können es nicht in die EntitätStudenteinfügen, da ein Student verschiedenen Kursen unterschiedliche Bewertungen geben kann. Ebenso wäre es keine gute Lösung, es in der EntitätCoursezu speichern.

Dies ist eine Situation, in derthe relationship itself has an attribute.

In diesem Beispiel sieht das Anhängen eines Attributs an eine Beziehung in einem ER-Diagramm folgendermaßen aus:

image

Wir können es fast genauso modellieren wie mit der einfachen Viele-zu-Viele-Beziehung. Der einzige Unterschied besteht darin, dass wir der Join-Tabelle ein neues Attribut hinzufügen:

image

3.2. Erstellen eines zusammengesetzten Schlüssels in JPA

Die Implementierung einer einfachen Viele-zu-Viele-Beziehung war recht unkompliziert. Das einzige Problem ist, dass wir einer Beziehung auf diese Weise keine Eigenschaft hinzufügen können, da wir die Entitäten direkt verbunden haben. Daher istwe had no way to add a property to the relationship itself.

Da wir DB-Attribute Klassenfeldern in JPA zuordnen,we need to create a new entity class for the relationship.

Natürlich benötigt jede JPA-Entität einen Primärschlüssel. Because our primary key is a composite key, we have to create a new class, which will hold the different parts of the key:

@Embeddable
class CourseRatingKey implements Serializable {

    @Column(name = "student_id")
    Long studentId;

    @Column(name = "course_id")
    Long courseId;

    // standard constructors, getters, and setters
    // hashcode and equals implementation
}

Beachten Sie, dass es einigekey requirements, which a composite key class has to fulfillgibt:

  • Wir müssen es mit@Embeddable markieren

  • Es mussjava.io.Serializable implementieren

  • Wir müssen eine Implementierung der Methodenhashcode() undequals() bereitstellen

  • Keines der Felder kann selbst eine Entität sein

3.3. Verwenden eines zusammengesetzten Schlüssels in JPA

Mit dieser zusammengesetzten Schlüsselklasse können wir die Entitätsklasse erstellen, die die Verknüpfungstabelle modelliert:

@Entity
class CourseRating {

    @EmbeddedId
    CourseRatingKey id;

    @ManyToOne
    @MapsId("student_id")
    @JoinColumn(name = "student_id")
    Student student;

    @ManyToOne
    @MapsId("course_id")
    @JoinColumn(name = "course_id")
    Course course;

    int rating;

    // standard constructors, getters, and setters
}

Dieser Code ist einer regulären Entitätsimplementierung sehr ähnlich. Wir haben jedoch einige wesentliche Unterschiede:

  • Wir haben@EmbeddedId, to mark the primary key verwendet, eine Instanz der KlasseCourseRatingKey

  • Wir haben die Felderstudent undcourse mit@MapsId markiert

@MapsId bedeutet, dass wir diese Felder an einen Teil des Schlüssels binden und sie die Fremdschlüssel einer Viele-zu-Eins-Beziehung sind. Wir brauchen es, weil wir, wie oben erwähnt, im zusammengesetzten Schlüssel keine Entitäten haben können.

Danach können wir die inversen Referenzen in den EntitätenStudent undCourse wie zuvor konfigurieren:

class Student {

    // ...

    @OneToMany(mappedBy = "student")
    Set ratings;

    // ...
}

class Course {

    // ...

    @OneToMany(mappedBy = "course")
    Set ratings;

    // ...
}

Beachten Sie, dass es eine alternative Möglichkeit gibt, zusammengesetzte Schlüssel zu verwenden: die Annotation@IdClass.

3.4. Weitere Merkmale

Wir haben die Beziehungen zu den KlassenStudent undCourse als@ManyToOne konfiguriert. Wir könnten dies tun, weil wir mit der neuen Entität die Viele-zu-Viele-Beziehung strukturell in zwei Viele-zu-Eins-Beziehungen zerlegt haben.

Warum konnten wir das tun? Wenn wir uns die Tabellen im vorherigen Fall genau ansehen, können wir sehen, dass sie zwei viele-zu-eins-Beziehungen enthielten. In other words, there isn’t any many-to-many relationship in an RDBMS. We call the structures we create with join tables many-to-many relationships because that’s what we model.

Außerdem ist es klarer, wenn wir über viele-zu-viele-Beziehungen sprechen, denn das ist unsere Absicht. In der Zwischenzeit ist eine Join-Tabelle nur ein Implementierungsdetail. es interessiert uns nicht wirklich.

Darüber hinaus verfügt diese Lösung über eine zusätzliche Funktion, die wir noch nicht erwähnt haben. Die einfache Viele-zu-Viele-Lösung stellt eine Beziehung zwischen zwei Entitäten her. Daher können wir die Beziehung nicht auf weitere Entitäten ausweiten. In dieser Lösung haben wir jedoch diese Grenze nicht:we can model relationships between any number of entity types.

Wenn beispielsweise mehrere Lehrer einen Kurs unterrichten können, können die Schüler bewerten, wie ein bestimmter Lehrer einen bestimmten Kurs unterrichtet. That way, a rating would be a relationship between three entities: a student, a course, and a teacher.

4. Viele zu vielen mit einer neuen Einheit

4.1. Modellierung von Beziehungsattributen

Nehmen wir an, wir möchten Studenten die Möglichkeit geben, sich für Kurse anzumelden. Auchwe need to store the point when a student registered for a specific course. Außerdem möchten wir speichern, welche Note sie im Kurs erhalten hat.

In einer idealen Welt könnten wir dies mit der vorherigen Lösung lösen, wenn wir eine Entität mit einem zusammengesetzten Schlüssel hätten. Unsere Welt ist jedoch alles andere als ideal und die Schüler absolvieren nicht immer beim ersten Versuch einen Kurs.

In diesem Fall gibt esmultiple connections between the same student-course pairs oder mehrere Zeilen mit denselbenstudent_id-course_id-Paaren. Wir können es mit keiner der vorherigen Lösungen modellieren, da alle Primärschlüssel eindeutig sein müssen. Daher müssen wir einen separaten Primärschlüssel verwenden.

Daherwe can introduce an entity, das die Attribute der Registrierung enthält:

image

In diesem Fallthe Registration entity represents the relationship zwischen den beiden anderen Entitäten.

Da es sich um eine Entität handelt, verfügt sie über einen eigenen Primärschlüssel.

Beachten Sie, dass wir in der vorherigen Lösung einen zusammengesetzten Primärschlüssel hatten, den wir aus den beiden Fremdschlüsseln erstellt haben. Jetzt sind die beiden Fremdschlüssel nicht mehr Teil des Primärschlüssels:

image

4.2. Implementierung in JPA

Dacoure_registration zu einer regulären Tabelle wurde, können wir eine einfache alte JPA-Entität erstellen, die sie modelliert:

@Entity
class CourseRegistration {

    @Id
    Long id;

    @ManyToOne
    @JoinColumn(name = "student_id")
    Student student;

    @ManyToOne
    @JoinColumn(name = "course_id")
    Course course;

    LocalDateTime registeredAt;

    int grade;

    // additional properties
    // standard constructors, getters, and setters
}

Außerdem müssen wir die Beziehungen in den KlassenStudent undCoursekonfigurieren:

class Student {

    // ...

    @OneToMany(mappedBy = "student")
    Set registrations;

    // ...
}

class Course {

    // ...

    @OneToMany(mappedBy = "courses")
    Set registrations;

    // ...
}

Wieder haben wir die Beziehung zuvor konfiguriert. Daher müssen wir JPA nur mitteilen, wo sie diese Konfiguration finden kann.

Beachten Sie, dass wir diese Lösung verwenden können, um das vorherige Problem zu lösen: Schüler, die Kurse bewerten. Es fühlt sich jedoch seltsam an, einen dedizierten Primärschlüssel zu erstellen, es sei denn, wir müssen. Darüber hinaus ist dies aus RDBMS-Sicht wenig sinnvoll, da die Kombination der beiden Fremdschlüssel einen perfekten zusammengesetzten Schlüssel ergibt. Außerdem ist dascomposite key had a clear meaning: which entities we connect in the relationship.

Ansonsten ist die Wahl zwischen diesen beiden Implementierungen oft nur eine persönliche Entscheidung.

5. Fazit

In diesem Tutorial haben wir gesehen, was eine Viele-zu-Viele-Beziehung ist und wie wir sie in einem RDBMS unter Verwendung von JPA modellieren können.

Wir haben drei Möglichkeiten gesehen, es in JPA zu modellieren. Alle drei haben unterschiedliche Vor- und Nachteile in Bezug auf:

  • Code-Klarheit

  • DB Klarheit

  • Fähigkeit, der Beziehung Attribute zuzuweisen

  • Wie viele Entitäten können wir mit der Beziehung verbinden und

  • Unterstützung für mehrere Verbindungen zwischen denselben Entitäten

Wie üblich sind die Beispiele inover on GitHub verfügbar.