Mapiranje nasljeđivanja hibernacije

1. Pregled

Relacijske baze podataka nemaju jednostavan način za mapiranje hijerarhija klasa na tablice baza podataka.

Da bi se to riješilo, JPA specifikacija nudi nekoliko strategija:

  • MappedSuperclass - roditeljske klase, ne mogu biti entiteti
  • Jedna tablica - entiteti iz različitih klasa sa zajedničkim pretkom smješteni su u jednu tablicu
  • Spojena tablica - svaka klasa ima svoju tablicu, a za upis entiteta podklase potrebno je spajanje tablica
  • Tablica po klasi - sva svojstva klase nalaze se u njezinoj tablici, tako da nije potrebno pridruživanje

Svaka strategija rezultira drugačijom strukturom baze podataka.

Nasljeđivanje entiteta znači da možemo koristiti polimorfne upite za dohvaćanje svih entiteta pod-klase prilikom upita za super-klasu.

Budući da je Hibernate JPA provedba, on sadrži sve gore navedeno kao i nekoliko značajki specifičnih za Hibernate povezane s nasljeđivanjem.

U sljedećim odjeljcima detaljnije ćemo razmotriti dostupne strategije.

2. MappedSuperclass

Koristiti MappedSuperclass strategija, nasljeđivanje je očito samo u klasi, ali ne i entitetski model.

Počnimo s izradom a Osoba razred koji će predstavljati roditeljski razred:

@MappedSuperclass javni razred Osoba {@Id private long personId; privatni naziv niza; // konstruktor, getteri, postavljači}

Primijetite da ova klasa više nema @ Entitet bilješka, jer se neće samostalno održavati u bazi podataka.

Dalje, dodajte an Zaposlenik potklasa:

@Entity javna klasa MyEfficiee proširuje Person {private String company; // konstruktor, getteri, postavljači}

U bazi podataka ovo će odgovarati jednoj “Moj zaposlenik” tablica s tri stupca za deklarirana i naslijeđena polja podklase.

Ako koristimo ovu strategiju, preci ne mogu sadržavati asocijacije s drugim entitetima.

3. Pojedinačni stol

Strategija pojedinačne tablice stvara jednu tablicu za svaku hijerarhiju klase. Ovo je također zadana strategija koju je odabrao JPA ako je ne izričito odredimo.

Strategiju koju želimo koristiti možemo definirati dodavanjem znaka @ Nasljeđivanje napomena za super-klasu:

@Entity @Inheritance (strategy = InheritanceType.SINGLE_TABLE) javna klasa MyProduct {@Id private long productId; privatni naziv niza; // konstruktor, getteri, postavljači}

Identifikator entiteta također je definiran u super-klasi.

Zatim možemo dodati entitete podklase:

@Entity public class book proširuje MyProduct {private String author; }
@Entity javna klasa olovka proširuje MyProduct {private String color; }

3.1. Vrijednosti diskriminacije

Budući da će zapisi za sve entitete biti u istoj tablici, Hibernate treba način da ih razlikuje.

Prema zadanim postavkama to se radi putem stupca diskriminator koji se naziva DTIP koja kao vrijednost ima naziv entiteta.

Da bismo prilagodili stupac diskriminator, možemo koristiti @DiscriminatorColumn napomena:

@Entity (name = "products") @Inheritance (strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn (name = "product_type", DisciminatorType = DiscriminatorType.INTEGER) javna klasa MyProduct {// ...}

Ovdje smo odlučili razlikovati MyProduct entiteti podklase od strane cijeli broj stupac pozvan Vrsta proizvoda.

Dalje, hibernaciji moramo reći koju će vrijednost imati svaki zapis podklase za Vrsta proizvoda stupac:

@Entity @DiscriminatorValue ("1") javna knjiga s knjigama proširuje MyProduct {// ...}
@Entity @DiscriminatorValue ("2") javna klasa olovke proširuje MyProduct {// ...}

Hibernate dodaje dvije druge unaprijed definirane vrijednosti koje bilješka može poprimiti: “null"I"nije null“:

  • @DiscriminatorValue (“null”) - znači da će se bilo koji redak bez vrijednosti razlikovanja preslikati u klasu entiteta s ovom bilješkom; to se može primijeniti na korijensku klasu hijerarhije
  • @DiscriminatorValue (“nije null”) - bilo koji redak s vrijednošću diskriminatora koji se ne podudara ni s jednim povezanim s definicijama entiteta preslikavat će se u klasu s ovom bilješkom

Umjesto stupca, možemo koristiti i hibernaciju @DiscriminatorFormula napomena za određivanje razlika vrijednosti:

@Entity @Inheritance (strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorFormula ("slučaj kada autor nije nula, a onda 1 drugo 2 kraj") javna klasa MyProduct {...}

Prednost ove strategije je u polimorfnim izvedbama upita jer se prilikom upita nadređenih entiteta mora pristupiti samo jednoj tablici. S druge strane, to ujedno znači i to više ne možemo koristiti NIJE NULJA ograničenja na podklasu svojstva entiteta.

4. Spojeni stol

Koristeći ovu strategiju, svaka klasa u hijerarhiji preslikava se u svoju tablicu. Jedini stupac koji se više puta pojavljuje u svim tablicama je identifikator koji će se koristiti za njihovo pridruživanje kada je potrebno.

Stvorimo super-klasu koja koristi ovu strategiju:

@Entity @Inheritance (strategy = InheritanceType.JOINED) javna klasa Animal {@Id private long animalId; privatne vrste gudača; // konstruktor, getteri, postavljači}

Tada možemo jednostavno definirati potklasu:

@Entity javni razred Pet proširuje Animal {private String name; // konstruktor, getteri, postavljači}

Obje će tablice imati animalId identifikator stupac. Primarni ključ Ljubimac entitet također ima ograničenje stranog ključa na primarni ključ svog matičnog entiteta. Da bismo prilagodili ovaj stupac, možemo dodati @PrimaryKeyJoinColumn napomena:

@Entity @PrimaryKeyJoinColumn (name = "petId") javni razred Ljubimac proširuje Animal {// ...}

Nedostatak ove metode mapiranja nasljeđa je taj što je za dohvaćanje entiteta potrebno spajanje između tablica, što može rezultirati manjim performansama za velik broj zapisa.

Broj pridruživanja veći je prilikom postavljanja upita roditeljskoj klasi jer će se pridružiti svakom pojedinom povezanom djetetu - pa je vjerojatnije da će utjecati na izvedbu to više što hijerarhijski želimo da dohvatimo zapise.

5. Tablica po razredu

Strategija Table Per Class preslikava svaki entitet na njegovu tablicu koja sadrži sva svojstva entiteta, uključujući ona naslijeđena.

Rezultirajuća shema slična je onoj koja se koristi @MappedSuperclass, ali za razliku od njega, tablica po klasi doista će definirati entitete za roditeljske klase, što kao rezultat omogućuje udruživanja i polimorfne upite.

Da bismo koristili ovu strategiju, trebamo samo dodati @ Nasljeđivanje napomena za osnovnu klasu:

@Entity @Inheritance (strategy = InheritanceType.TABLE_PER_CLASS) vozilo javne klase {@Id private long vehicleId; privatni proizvođač žica; // standardni konstruktor, getteri, postavljači}

Tada možemo stvoriti potklase na standardni način.

To se ne razlikuje mnogo od pukog mapiranja svakog entiteta bez nasljeđivanja. Razlika je očita kada se traži osnovna klasa, koja će vratiti i sve zapise podklase upotrebom a UNIJA izjava u pozadini.

Korištenje UNIJA također može dovesti do slabije izvedbe pri odabiru ove strategije. Drugo je pitanje što više ne možemo koristiti generiranje ključa identiteta.

6. Polimorfni upiti

Kao što je spomenuto, upit osnovne klase dohvatit će i sve entitete potklase.

Pogledajmo ovo ponašanje na djelu s JUnit testom:

@Test javna praznina givenSubclasses_whenQuerySuperclass_thenOk () {Book book = nova knjiga (1, "1984", "George Orwell"); session.save (knjiga); Olovka za olovku = nova olovka (2, "moja olovka", "plava"); session.save (olovka); assertThat (session.createQuery ("iz MyProduct") .getResultList ()). hasSize (2); }

U ovom smo primjeru stvorili dva Knjiga i Pen predmeta, a zatim su postavili upit za njihovu super-klasu MyProduct kako bismo potvrdili da ćemo dohvatiti dva objekta.

Hibernate također može tražiti sučelja ili osnovne klase koje nisu entiteti, ali ih proširuju ili implementiraju klase entiteta. Pogledajmo JUnit test koristeći naš @MappedSuperclass primjer:

@Test javna praznina givenSubclasses_whenQueryMappedSuperclass_thenOk () {MyEfficiee emp = new MyEfficiee (1, "john", "baeldung"); session.save (emp); assertThat (session.createQuery ("iz com.baeldung.hibernate.pojo.inheritance.Person") .getResultList ()) .hasSize (1); }

Imajte na umu da ovo također radi za bilo koju super-klasu ili sučelje, bilo da se radi o @MappedSuperclass ili ne. Razlika od uobičajenog HQL upita je u tome što moramo koristiti potpuno kvalificirano ime jer nisu entiteti kojima upravlja Hibernate.

Ako ne želimo da podvrsta bude vraćena ovom vrstom upita, trebamo dodati samo hibernaciju @ Polimorfizam bilješka uz njezinu definiciju, s tipom EKSPLICITAN:

@Entity @Polymorphism (type = PolymorphismType.EXPLICIT) Javna klasa Torba implementira stavku {...}

U ovom slučaju, prilikom upita za Predmeti, the Torba zapisi se neće vratiti.

7. Zaključak

U ovom smo članku prikazali različite strategije mapiranja nasljeđivanja u hibernaciji.

Potpuni izvorni kod primjera može se naći na GitHubu.