Kortlægning af en enkelt enhed til flere tabeller i JPA

Udholdenhedstop

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

1. Introduktion

JPA gør håndtering af relationelle databasemodeller fra vores Java-applikationer mindre smertefuldt. Ting er enkle, når vi kortlægger hver tabel til en enkelt enhedsklasse. Men nogle gange har vi grunde til at modellere vores enheder og tabeller forskelligt:

  • Når vi vil oprette logiske grupper af felter, kan vi kortlægge flere klasser til en enkelt tabel
  • Hvis arv er involveret, kan vi kortlægge et klasseshierarki til en tabelstruktur
  • I tilfælde, når relaterede felter er spredt mellem flere tabeller, og vi vil modellere disse tabeller med en enkelt klasse

I denne korte vejledning ser vi, hvordan vi tackler dette sidste scenario.

2. Datamodel

Lad os sige, at vi driver en restaurant, og vi vil gemme data om hvert måltid, vi serverer:

  • navn
  • beskrivelse
  • pris
  • hvilken slags allergener den indeholder

Da der er mange mulige allergener, grupperer vi dette datasæt. Desuden modellerer vi dette ved hjælp af følgende tabeldefinitioner:

Lad os nu se, hvordan vi kan kortlægge disse tabeller til enheder ved hjælp af standard JPA-kommentarer.

3. Oprettelse af flere enheder

Den mest oplagte løsning er at oprette en enhed til begge klasser.

Lad os starte med at definere Måltid enhed:

@Entity @Table (name = "meal") klasse Måltid {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) @Column (name = "id") Lang id; @Kolonne (navn = "navn") Navn på streng; @Kolonne (navn = "beskrivelse") Beskrivelse af streng; @Column (name = "pris") BigDecimal pris; @OneToOne (mappedBy = "måltid") Allergener allergener; // standard getters og setter}

Derefter tilføjer vi Allergener enhed:

@Entity @Table (name = "allergens") klasse Allergener {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) @Column (name = "meal_id") Lang måltidId; @OneToOne @PrimaryKeyJoinColumn (name = "meal_id") Måltidsmåltid; @Column (name = "peanuts") boolske peanuts; @Column (name = "selleri") boolsk selleri; @Column (name = "sesame_seeds") boolsk sesameSeeds; // standard getters og setter}

I ovenstående eksempel kan vi se det måltid_id er både den primære nøgle og også den fremmede nøgle. Det betyder, at vi skal definere en-til-en-forholdskolonnen ved hjælp af @PrimaryKeyJoinColumn.

Denne løsning har dog to problemer:

  • Vi vil altid opbevare allergener til et måltid, og denne løsning håndhæver ikke denne regel
  • Måltids- og allergendataene hører logisk sammen - derfor vil vi måske gemme disse oplysninger i samme Java-klasse, selvom vi oprettede flere tabeller til dem

En mulig løsning på det første problem er at tilføje @NotNull kommentar til allergener felt på vores Måltid enhed. JPA vil ikke lade os fortsætte Måltid hvis vi har en nulAllergener.

Dette er dog ikke en ideel løsning; vi ønsker en mere restriktiv, hvor vi ikke engang har mulighed for at forsøge at fortsætte en Måltid uden Allergener.

4. Oprettelse af en enkelt enhed med @SecondaryTable

Vi kan oprette en enkelt enhed, der angiver, at vi har kolonner i forskellige tabeller ved hjælp af @SecondaryTable kommentar:

@Entity @Table (name = "meal") @SecondaryTable (name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn (name = "meal_id")) klasse Måltid {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) @Column (navn = "id") Langt id; @Kolonne (navn = "navn") Navn på streng; @Kolonne (navn = "beskrivelse") Beskrivelse af streng; @Column (name = "pris") BigDecimal pris; @Column (name = "jordnødder", tabel = "allergener") boolske jordnødder; @Column (name = "selleri", tabel = "allergener") boolsk selleri; @Column (name = "sesame_seeds", tabel = "allergener") boolean sesameSeeds; // standard getters og setter}

Bag kulisserne forbinder JPA den primære tabel med den sekundære tabel og udfylder felterne. Denne løsning svarer til @En til en forhold, men på denne måde kan vi have alle egenskaberne i samme klasse.

Det er vigtigt at bemærke dethvis vi har en kolonne, der er i en sekundær tabel, skal vi specificere den med bord argument af @Kolonne kommentar. Hvis en kolonne er i den primære tabel, kan vi udelade bord argument som JPA ser som standard på kolonner i den primære tabel.

Bemærk også, at vi kan have flere sekundære tabeller, hvis vi integrerer dem i @SecondaryTables. Alternativt kan vi fra Java 8 markere enheden med flere @SecondaryTable kommentarer, da det er en gentagelig kommentar.

5. Kombination @SecondaryTable Med @Embedded

Som vi har set, @SecondaryTable kortlægger flere tabeller til den samme enhed. Det ved vi også @Embedded og @Kan integreres at gøre det modsatte og kortlægge en enkelt tabel til flere klasser.

Lad os se, hvad vi får, når vi kombinerer @SecondaryTable med @Embedded og @Embeddable:

@Entity @Table (name = "meal") @SecondaryTable (name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn (name = "meal_id")) klasse Måltid {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) @Column (navn = "id") Langt id; @Kolonne (navn = "navn") Navn på streng; @Kolonne (navn = "beskrivelse") Beskrivelse af streng; @Column (name = "pris") BigDecimal pris; @Embedded Allergens allergens; // standard getters and setters} @Embeddable class Allergens {@Column (name = "peanuts", table = "allergens") boolean peanuts; @Column (name = "selleri", tabel = "allergener") boolsk selleri; @Column (name = "sesame_seeds", tabel = "allergener") boolsk sesameSeeds; // standard getters og setter}

Det er en lignende tilgang til det, vi så ved hjælp af @En til en. Det har dog et par fordele:

  • JPA administrerer de to borde sammen for os, så vi kan være sikre på, at der vil være en række for hvert måltid i begge borde
  • Koden er også lidt enklere, da vi har brug for mindre konfiguration

Ikke desto mindre fungerer denne en-til-en-lignende løsning kun, når de to tabeller har matchende id'er.

Det er værd at nævne, at hvis vi ønsker at genbruge Allergener klasse, ville det være bedre, hvis vi definerede kolonnerne i den sekundære tabel i Måltid klasse med @AttributeOverride.

6. Konklusion

I denne korte vejledning har vi set, hvordan vi kan kortlægge flere tabeller til den samme enhed ved hjælp af @SecondaryTable JPA-kommentar.

Vi så også fordelene ved at kombinere @SecondaryTable med @Embedded og @Embeddable for at få et forhold, der ligner en-til-en.

Som sædvanligt er eksemplerne tilgængelige på GitHub.

Persistens bund

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN