Zuordnen einer einzelnen Entität zu mehreren Tabellen in JPA

Ausdauer oben

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Einleitung

JPA macht den Umgang mit relationalen Datenbankmodellen aus unseren Java-Anwendungen weniger schmerzhaft. Die Dinge sind einfach, wenn wir jede Tabelle einer einzelnen Entitätsklasse zuordnen. Manchmal haben wir jedoch Gründe, unsere Entitäten und Tabellen anders zu modellieren:

  • Wenn wir logische Gruppen von Feldern erstellen möchten, können wir mehrere Klassen einer einzelnen Tabelle zuordnen
  • Wenn es sich um eine Vererbung handelt, können wir eine Klassenhierarchie einer Tabellenstruktur zuordnen
  • In Fällen, in denen verwandte Felder auf mehrere Tabellen verteilt sind und wir diese Tabellen mit einer einzelnen Klasse modellieren möchten

In diesem kurzen Tutorial erfahren Sie, wie Sie dieses letzte Szenario angehen können.

2. Datenmodell

Nehmen wir an, wir betreiben ein Restaurant und möchten Daten zu jeder Mahlzeit speichern, die wir servieren:

  • Name
  • Beschreibung
  • Preis
  • welche Art von Allergenen es enthält

Da es viele mögliche Allergene gibt, werden wir diesen Datensatz zusammenfassen. Darüber hinaus werden wir dies auch anhand der folgenden Tabellendefinitionen modellieren:

Nun wollen wir sehen, wie wir diese Tabellen mithilfe von Standard-JPA-Annotationen Entitäten zuordnen können.

3. Mehrere Entitäten erstellen

Die naheliegendste Lösung besteht darin, eine Entität für beide Klassen zu erstellen.

Beginnen wir mit der Definition der Mahlzeitentität :

@Entity @Table(name = "meal") class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @OneToOne(mappedBy = "meal") Allergens allergens; // standard getters and setters }

Als nächstes fügen wir die Entität Allergene hinzu :

@Entity @Table(name = "allergens") class Allergens { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "meal_id") Long mealId; @OneToOne @PrimaryKeyJoinColumn(name = "meal_id") Meal meal; @Column(name = "peanuts") boolean peanuts; @Column(name = "celery") boolean celery; @Column(name = "sesame_seeds") boolean sesameSeeds; // standard getters and setters }

Im obigen Beispiel sehen wir, dass Mahlzeit_ID sowohl der Primärschlüssel als auch der Fremdschlüssel ist. Das heißt, wir müssen die Eins-zu-Eins-Beziehungsspalte mit @PrimaryKeyJoinColumn definieren .

Diese Lösung weist jedoch zwei Probleme auf:

  • Wir möchten immer Allergene für eine Mahlzeit aufbewahren, und diese Lösung setzt diese Regel nicht durch
  • Die Mahlzeit- und Allergendaten gehören logisch zusammen. Daher möchten wir diese Informationen möglicherweise in derselben Java-Klasse speichern, obwohl wir mehrere Tabellen für sie erstellt haben

Eine mögliche Lösung für das erste Problem besteht darin, die Annotation @NotNull zum Feld Allergene in unserer Mahlzeiteinheit hinzuzufügen . JPA wird uns nicht bestehen bleiben , die lassen Mahlzeit , wenn wir eine haben null Allergenen .

Dies ist jedoch keine ideale Lösung. Wir wollen eine restriktivere, bei der wir nicht einmal die Möglichkeit haben, eine Mahlzeit ohne Allergene fortzusetzen.

4. Erstellen einer einzelnen Entität mit @SecondaryTable

Mit der Annotation @SecondaryTable können wir eine einzelne Entität erstellen, die angibt, dass wir Spalten in verschiedenen Tabellen haben :

@Entity @Table(name = "meal") @SecondaryTable(name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn(name = "meal_id")) class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @Column(name = "peanuts", table = "allergens") boolean peanuts; @Column(name = "celery", table = "allergens") boolean celery; @Column(name = "sesame_seeds", table = "allergens") boolean sesameSeeds; // standard getters and setters }

Hinter den Kulissen verbindet JPA die Primärtabelle mit der Sekundärtabelle und füllt die Felder. Diese Lösung ähnelt der @ OneToOne- Beziehung, aber auf diese Weise können wir alle Eigenschaften in derselben Klasse haben.

Es ist wichtig zu beachten, dass eine Spalte, die sich in einer sekundären Tabelle befindet, mit dem Tabellenargument der Annotation @Column angegeben werden muss . Wenn sich eine Spalte in der Primärtabelle befindet, können wir das Tabellenargument weglassen, da JPA standardmäßig nach Spalten in der Primärtabelle sucht.

Beachten Sie außerdem, dass wir mehrere sekundäre Tabellen haben können, wenn wir sie in @SecondaryTables einbetten . Alternativ können wir ab Java 8 die Entität mit mehreren @ SecondaryTable- Annotationen markieren, da es sich um eine wiederholbare Annotation handelt.

5. Kombinieren von @SecondaryTable mit @Embedded

Wie wir gesehen haben, ordnet @SecondaryTable mehrere Entitäten derselben Entität zu. Wir wissen auch, dass @Embedded und @ Embeddable das Gegenteil tun und eine einzelne Tabelle mehreren Klassen zuordnen .

Mal sehen, was wir bekommen, wenn wir @SecondaryTable mit @Embedded und @Embeddable kombinieren :

@Entity @Table(name = "meal") @SecondaryTable(name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn(name = "meal_id")) class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @Embedded Allergens allergens; // standard getters and setters } @Embeddable class Allergens { @Column(name = "peanuts", table = "allergens") boolean peanuts; @Column(name = "celery", table = "allergens") boolean celery; @Column(name = "sesame_seeds", table = "allergens") boolean sesameSeeds; // standard getters and setters }

Es ist ein ähnlicher Ansatz wie bei @OneToOne . Es hat jedoch einige Vorteile:

  • JPA verwaltet die beiden Tische gemeinsam für uns, sodass wir sicher sein können, dass für jede Mahlzeit in beiden Tischen eine Zeile vorhanden ist
  • Außerdem ist der Code etwas einfacher, da wir weniger Konfiguration benötigen

Trotzdem funktioniert diese Eins-zu-Eins-ähnliche Lösung nur, wenn die beiden Tabellen übereinstimmende IDs haben.

Es ist erwähnenswert, dass es besser ist, wenn wir die Allergens- Klasse wiederverwenden möchten, die Spalten der sekundären Tabelle in der Meal- Klasse mit @AttributeOverride zu definieren .

6. Fazit

In diesem kurzen Tutorial haben wir gesehen, wie wir mithilfe der JPA-Annotation @SecondaryTable mehrere Tabellen derselben Entität zuordnen können .

Wir haben auch die Vorteile der Kombination von @SecondaryTable mit @Embedded und @Embeddable gesehen , um eine ähnliche Beziehung wie eins zu eins zu erhalten.

Wie üblich sind die Beispiele auf GitHub verfügbar.

Persistenz unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs