Een enkele entiteit toewijzen aan meerdere tabellen in JPA

Persistentie top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS

1. Inleiding

JPA maakt het omgaan met relationele databasemodellen van onze Java-applicaties minder pijnlijk. Het is eenvoudig als we elke tabel toewijzen aan een enkele entiteitsklasse. Maar soms hebben we redenen om onze entiteiten en tabellen anders te modelleren:

  • Als we logische groepen velden willen maken, kunnen we meerdere klassen aan een enkele tabel toewijzen
  • Als er sprake is van overerving, kunnen we een klassehiërarchie toewijzen aan een tabelstructuur
  • In gevallen waarin gerelateerde velden zijn verspreid over meerdere tabellen, en we die tabellen willen modelleren met een enkele klasse

In deze korte tutorial zullen we zien hoe we dit laatste scenario kunnen aanpakken.

2. Gegevensmodel

Stel dat we een restaurant runnen en we willen gegevens opslaan over elke maaltijd die we serveren:

  • naam
  • Omschrijving
  • prijs
  • wat voor soort allergenen het bevat

Omdat er veel mogelijke allergenen zijn, gaan we deze dataset groeperen. Verder zullen we dit ook modelleren met behulp van de volgende tabeldefinities:

Laten we nu eens kijken hoe we deze tabellen kunnen toewijzen aan entiteiten met behulp van standaard JPA-annotaties.

3. Meerdere entiteiten maken

De meest voor de hand liggende oplossing is om een ​​entiteit voor beide klassen te maken.

Laten we beginnen met het definiëren van de Maaltijd entiteit:

@Entity @Table (naam = "maaltijd") klasse Maaltijd {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) @Column (naam = "id") Lange id; @Column (name = "name") Stringnaam; @Column (name = "description") Stringbeschrijving; @Column (name = "price") BigDecimal-prijs; @OneToOne (mappedBy = "meal") Allergenen allergenen; // standaard getters en setters}

Vervolgens voegen we de Allergenen entiteit:

@Entity @Table (name = "allergenen") klasse Allergenen {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) @Column (name = "meal_id") Lange mealId; @OneToOne @PrimaryKeyJoinColumn (name = "meal_id") Maaltijd maaltijd; @Column (name = "peanuts") booleaanse pinda's; @Column (name = "selderij") booleaanse selderij; @Column (name = "sesame_seeds") booleaanse sesameSeeds; // standaard getters en setters}

In het bovenstaande voorbeeld kunnen we dat zien meal_id is zowel de primaire sleutel als de externe sleutel. Dat betekent dat we de één-op-één-relatiekolom moeten definiëren met @BuienRadarNL.

Deze oplossing heeft echter twee problemen:

  • We willen altijd allergenen bewaren voor een maaltijd, en deze oplossing dwingt deze regel niet af
  • De maaltijd- en allergeengegevens horen logischerwijs bij elkaar - daarom willen we deze informatie misschien in dezelfde Java-klasse opslaan, ook al hebben we er meerdere tabellen voor gemaakt

Een mogelijke oplossing voor het eerste probleem is om het @Niet nul annotatie bij de allergenen veld op onze Maaltijd entiteit. De PPV laat ons het Maaltijd als we een nulAllergenen.

Dit is echter geen ideale oplossing; we willen een meer beperkende, waar we niet eens de mogelijkheid hebben om te proberen een Maaltijd zonder Allergenen.

4. Een enkele entiteit maken met @BuienRadarNL

We kunnen een enkele entiteit maken die specificeert dat we kolommen in verschillende tabellen hebben met behulp van de @BuienRadarNL annotatie:

@Entity @Table (name = "meal") @SecondaryTable (name = "allergenen", pkJoinColumns = @PrimaryKeyJoinColumn (name = "meal_id")) klasse Maaltijd {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) @Column (naam = "id") Lange id; @Column (name = "name") Stringnaam; @Column (name = "description") Stringbeschrijving; @Column (name = "price") BigDecimal-prijs; @Column (name = "peanuts", table = "allergenen") booleaanse pinda's; @Column (name = "selderij", table = "allergenen") booleaanse selderij; @Column (name = "sesame_seeds", table = "allergenen") boolean sesameSeeds; // standaard getters en setters}

Achter de schermen voegt JPA zich bij de primaire tabel met de secundaire tabel en vult de velden. Deze oplossing is vergelijkbaar met de @Een op een relatie, maar op deze manier kunnen we alle eigenschappen in dezelfde klasse hebben.

Het is belangrijk om dat op te merkenals we een kolom hebben die in een secundaire tabel staat, moeten we deze specificeren met de tafel argument van de @Kolom annotatie. Als een kolom in de primaire tabel staat, kunnen we de tafel argument omdat JPA standaard naar kolommen in de primaire tabel zoekt.

Merk ook op dat we meerdere secundaire tabellen kunnen hebben als we ze insluiten in @BuienRadarNL. Als alternatief kunnen we vanuit Java 8 de entiteit met meerdere markeren @BuienRadarNL annotaties omdat het een herhaalbare annotatie is.

5. Combineren @BuienRadarNL Met @Embedded

Zoals we hebben gezien, @BuienRadarNL wijst meerdere tabellen toe aan dezelfde entiteit. Dat weten we ook @Embedded en @Insluitbaar om het tegenovergestelde te doen en een enkele tabel aan meerdere klassen toe te wijzen.

Laten we eens kijken wat we krijgen als we combineren @BuienRadarNL met @Embedded en @Embeddable:

@Entity @Table (name = "meal") @SecondaryTable (name = "allergenen", pkJoinColumns = @PrimaryKeyJoinColumn (name = "meal_id")) klasse Maaltijd {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) @Column (naam = "id") Lange id; @Column (name = "name") Stringnaam; @Column (name = "description") Stringbeschrijving; @Column (name = "price") BigDecimal-prijs; @Embedded Allergenen allergenen; // standaard getters en setters} @Embeddable klasse Allergenen {@Column (name = "peanuts", table = "allergenen") booleaanse pinda's; @Column (name = "selderij", table = "allergenen") booleaanse selderij; @Column (name = "sesame_seeds", table = "allergenen") boolean sesameSeeds; // standaard getters en setters}

Het is een vergelijkbare benadering als wat we zagen met @Een op een. Het heeft echter een aantal voordelen:

  • JPA beheert de twee tafels samen voor ons, dus we kunnen er zeker van zijn dat er voor elke maaltijd een rij in beide tafels is
  • De code is ook een beetje eenvoudiger, omdat we minder configuratie nodig hebben

Desalniettemin werkt deze één-op-één-achtige oplossing alleen als de twee tabellen overeenkomende ID's hebben.

Het is de moeite waard te vermelden dat als we het Allergenen class, zou het beter zijn als we de kolommen van de secundaire tabel in de Maaltijd les met @AttributeOverride.

6. Conclusie

In deze korte tutorial hebben we gezien hoe we meerdere tabellen aan dezelfde entiteit kunnen toewijzen met behulp van de @BuienRadarNL JPA-annotatie.

We zagen ook de voordelen van combineren @BuienRadarNL met @Embedded en @Embeddable om een ​​relatie te krijgen die vergelijkbaar is met een-op-een.

Zoals gewoonlijk zijn de voorbeelden beschikbaar op GitHub.

Persistentie onderaan

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS