Dvaletilstand Kortlægning af arv

1. Oversigt

Relationsdatabaser har ikke en ligetil måde at kortlægge klassehierarkier på databasetabeller.

For at løse dette indeholder JPA-specifikationen flere strategier:

  • Kortlagt superklasse - overordnede klasser kan ikke være enheder
  • Enkeltbord - enhederne fra forskellige klasser med en fælles forfader er placeret i en enkelt tabel
  • Joined Table - hver klasse har sin tabel, og forespørgsel efter en underklasseenhed kræver sammenføjning til tabellerne
  • Tabel pr. Klasse - alle klassens egenskaber findes i dens tabel, så der kræves ingen sammenføjning

Hver strategi resulterer i en anden databasestruktur.

Enhedsarv betyder, at vi kan bruge polymorfe forespørgsler til at hente alle underklasseenheder, når vi spørger efter en superklasse.

Da dvale er en JPA-implementering, indeholder den alle ovenstående samt et par dvale-specifikke funktioner relateret til arv.

I de næste sektioner gennemgår vi tilgængelige strategier mere detaljeret.

2. Kortlagt superklasse

Bruger Kortlagt superklasse strategi, arv er kun tydelig i klassen, men ikke enhedsmodellen.

Lad os starte med at oprette en Person klasse, der repræsenterer en overordnet klasse:

@MappedSuperclass offentlig klasse person {@Id privat lang personId; privat strengnavn; // konstruktør, getters, setters}

Bemærk, at denne klasse ikke længere har en @Enhed kommentar, da det ikke vedholdes i selve databasen.

Lad os derefter tilføje en Medarbejder underklasse:

@Entity offentlig klasse MyEmployee udvider Person {privat strengfirma; // konstruktør, getters, setters}

I databasen svarer dette til en “Min medarbejder” tabel med tre kolonner til deklarerede og nedarvede felter i underklassen.

Hvis vi bruger denne strategi, kan forfædre ikke indeholde tilknytning til andre enheder.

3. Enkeltbord

Strategien Single Table opretter en tabel for hvert klassehierarki. Dette er også standardstrategien, som JPA vælger, hvis vi ikke angiver en eksplicit.

Vi kan definere den strategi, vi vil bruge, ved at tilføje @Arv kommentar til superklassen:

@Entity @Inheritance (strategi = InheritanceType.SINGLE_TABLE) offentlig klasse MyProduct {@Id privat langt produktId; privat strengnavn; // konstruktør, getters, setters}

Enhedernes identifikator er også defineret i superklassen.

Derefter kan vi tilføje underklassenheder:

@Entity public class Bog udvider MyProduct {private Stringforfatter; }
@Entity public class Pen udvider MyProduct {privat strengfarve; }

3.1. Diskriminatorværdier

Da posterne for alle enheder vil være i den samme tabel, Dvaletilstand har brug for en måde at skelne mellem dem.

Som standard gøres dette gennem en diskriminatorkolonne kaldet DTYPE som har navnet på enheden som en værdi.

For at tilpasse diskriminatorkolonnen kan vi bruge @DiscriminatorColumn kommentar:

@Entity (name = "products") @ Arv (strategi = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn (name = "product_type", discriminatorType = DiscriminatorType.INTEGER) offentlig klasse MyProduct {// ...}

Her har vi valgt at differentiere MyProduct underklassenheder af en heltal kolonne kaldet produkttype.

Derefter skal vi fortælle dvale, hvilken værdi hver underklassepost vil have for produkttype kolonne:

@Entity @DiscriminatorValue ("1") offentlig klasse Bog udvider MyProduct {// ...}
@Entity @DiscriminatorValue ("2") offentlig klasse Pen udvider MyProduct {// ...}

Dvaletilstand tilføjer to andre foruddefinerede værdier, som kommentaren kan tage: “nul”Og“ikke null“:

  • @DiscriminatorValue (“null”) - betyder, at enhver række uden en diskriminerende værdi kortlægges til enhedsklassen med denne kommentar; dette kan anvendes til rodklassen i hierarkiet
  • @DiscriminatorValue (“ikke null”) - enhver række med en diskriminatorværdi, der ikke matcher nogen af ​​dem, der er knyttet til enhedsdefinitioner, kortlægges til klassen med denne kommentar

I stedet for en kolonne kan vi også bruge den specifikke dvale @DiscriminatorFormula kommentar for at bestemme de differentierende værdier:

@Entity @Inheritance (strategi = InheritanceType.SINGLE_TABLE) @DiscriminatorFormula ("tilfælde, hvor forfatteren ikke er nul, så 1 anden 2 slutter") offentlig klasse MyProduct {...}

Denne strategi har fordelen ved polymorf forespørgsel, da der kun er adgang til en tabel, når der spørges til overordnede enheder. På den anden side betyder det også, at vi kan ikke længere bruge IKKE NULL begrænsninger for underklasse enhedsegenskaber.

4. Sammenføjet tabel

Ved hjælp af denne strategi kortlægges hver klasse i hierarkiet til bordet. Den eneste kolonne, der gentagne gange vises i alle tabeller, er identifikatoren, som vil blive brugt til at slutte sig til dem, når det er nødvendigt.

Lad os oprette en superklasse, der bruger denne strategi:

@Entity @Inheritance (strategi = InheritanceType.JOINED) offentlig klasse Animal {@Id private long animalId; private String arter; // konstruktør, getters, setters}

Derefter kan vi blot definere en underklasse:

@Entity offentlig klasse Pet extends Animal {private String name; // konstruktør, getters, setters}

Begge tabeller har en animalId identifikationskolonne. Den primære nøgle til Kæledyr enhed har også en fremmed nøglebegrænsning til den primære nøgle for dets modervirksomhed. For at tilpasse denne kolonne kan vi tilføje @PrimaryKeyJoinColumn kommentar:

@Entity @PrimaryKeyJoinColumn (name = "petId") offentlig klasse Kæledyr udvider dyr {// ...}

Ulempen ved denne arvemappningsmetode er, at hentning af enheder kræver sammenkædninger mellem tabeller, hvilket kan resultere i lavere ydeevne for et stort antal poster.

Antallet af sammenføjninger er højere, når der stilles en forespørgsel til forældreklassen, da den vil slutte sig til hvert enkelt relateret barn - så præstation er mere tilbøjelige til at blive påvirket, jo højere op i hierarkiet, vi vil hente poster.

5. Tabel pr. Klasse

Tabel pr. Klasse-strategi kortlægger hver enhed til sin tabel, som indeholder alle enhedens egenskaber, inklusive de arvede.

Det resulterende skema svarer til det, der bruges @MappedSuperclass, men i modsætning til det vil tabel pr. klasse faktisk definere enheder til overordnede klasser, hvilket tillader associationer og polymorfe forespørgsler som et resultat.

For at bruge denne strategi behøver vi kun tilføje @Arv kommentar til basisklassen:

@Entity @Inheritance (strategi = InheritanceType.TABLE_PER_CLASS) offentlig klasse køretøj {@Id privat lang køretøjId; privat streng producent; // standard konstruktør, getters, setters}

Derefter kan vi oprette underklasser på standard måde.

Dette adskiller sig ikke meget fra blot at kortlægge hver enhed uden arv. Sondringen er tydelig ved forespørgsel på baseklassen, som også returnerer alle underklasseposter ved hjælp af a UNION udsagn i baggrunden.

Brugen af UNION kan også føre til dårligere præstationer, når du vælger denne strategi. Et andet problem er, at vi ikke længere kan bruge identitetsnøglegenerering.

6. Polymorfe forespørgsler

Som nævnt vil forespørgsel efter en basisklasse også hente alle underklassenheder.

Lad os se denne adfærd i aktion med en JUnit-test:

@Test offentligt ugyldigt givenSubclasses_whenQuerySuperclass_thenOk () {Book book = new Book (1, "1984", "George Orwell"); session.save (bog); Pen pen = ny pen (2, "min pen", "blå"); session.save (pen); assertThat (session.createQuery ("fra MyProduct") .getResultList ()). hasSize (2); }

I dette eksempel har vi oprettet to Bestil og Pen genstande, forespurgte derefter deres superklasse MyProduct for at kontrollere, at vi henter to objekter.

Dvaletilstand kan også forespørge på grænseflader eller baseklasser, som ikke er enheder, men som udvides eller implementeres af enhedsklasser. Lad os se en JUnit-test ved hjælp af vores @MappedSuperclass eksempel:

@Test offentligt ugyldigt givenSubclasses_whenQueryMappedSuperclass_thenOk () {MyEmployee emp = new MyEmployee (1, "john", "baeldung"); session.save (emp); assertThat (session.createQuery ("fra com.baeldung.hibernate.pojo.inheritance.Person") .getResultList ()) .hasSize (1); }

Bemærk, at dette også fungerer for enhver superklasse eller interface, uanset om det er en @MappedSuperclass eller ikke. Forskellen fra en almindelig HQL-forespørgsel er, at vi skal bruge det fuldt kvalificerede navn, da de ikke er dvalestyrede enheder.

Hvis vi ikke ønsker, at en underklasse skal returneres af denne type forespørgsel, behøver vi kun tilføje dvale @Polymorfisme kommentar til dens definition med type EKSPLIKATION:

@Entity @Polymorphism (type = PolymorphismType.EXPLICIT) public class Bag implementer Item {...}

I dette tilfælde når du spørger efter Varer, det Taske poster returneres ikke.

7. Konklusion

I denne artikel har vi vist de forskellige strategier til kortlægning af arv i dvale.

Den fulde kildekode for eksemplerne kan findes på GitHub.