Prvá polovica tohto tutoriálu predstavila základy rozhrania Java Persistence API a ukázala vám, ako nakonfigurovať aplikáciu JPA pomocou Hibernate 5.3.6 a Java 8. Ak ste si prečítali tento tutoriál a preštudovali si jeho ukážkovú aplikáciu, potom poznáte základy modelovanie entít JPA a vzťahov typu dvojice k jedným v JPA. Mali ste tiež skúsenosti s písaním pomenovaných dotazov pomocou jazyka JPA Query Language (JPQL).
V tejto druhej polovici tutoriálu pôjdeme hlbšie k JPA a Hibernate. Dozviete sa, ako modelovať vzájomné vzťahy medzi mnohými Film
a SuperHero
entít, zriadiť pre tieto entity jednotlivé úložiská a uchovať ich v databáze H2 v pamäti. Dozviete sa tiež viac o úlohe kaskádových operácií v JPA a získate tipy na výber a CascadeType
stratégia pre subjekty v databáze. Na záver spojíme fungujúcu aplikáciu, ktorú môžete spustiť vo svojom IDE alebo na príkazovom riadku.
Tento výukový program sa zameriava na základy JPA, ale nezabudnite si prečítať tieto tipy pre Javu, ktoré zavádzajú pokročilejšie témy v JPA:
- Dedičské vzťahy v JPA a Hibernate
- Kompozitné kľúče v JPA a Hibernate
Vzťahy typu many-to-many v JPA
Vzťahy medzi mnohými definujte entity, pre ktoré môžu mať obe strany vzťahu viac vzájomných odkazov. Pre náš príklad budeme modelovať filmy a superhrdinov. Na rozdiel od príkladu Autori a knihy z 1. časti môže mať film viacerých superhrdinov a superhrdina sa môže objaviť vo viacerých filmoch. Naši superhrdinovia, Ironman a Thor, sa objavujú v dvoch filmoch „The Avengers“ a „Avengers: Infinity War“.
Na modelovanie tohto vzťahu medzi mnohými pomocou JPA budeme potrebovať tri tabuľky:
- FILM
- SUPER_HERO
- SUPERHERO_MOVIES
Obrázok 1 zobrazuje doménový model s tromi tabuľkami.

Poznač si to SuperHero_Movies
je a pripojiť sa k stolu medzi Film
a SuperHero
stoly. V JPA je spojovacia tabuľka špeciálny druh tabuľky, ktorá uľahčuje vzájomné vzťahy medzi mnohými.
Jednosmerný alebo obojsmerný?
V JPA používame @ManyToMany
anotácia na modelovanie vzťahov medzi mnohými. Tento typ vzťahu môže byť jednosmerný alebo obojsmerný:
- V jednosmerný vzťah iba jedna entita vo vzťahu ukazuje na druhú.
- V obojsmerný vzťah obidva subjekty ukazujú na seba.
Náš príklad je obojsmerný, čo znamená, že film ukazuje na všetkých svojich superhrdinov a superhrdina ukazuje na všetky svoje filmy. V obojsmernom vzťahu mnoho-veľa jedna entita vlastné vzťah a druhý je mapované na vzťah. Používame mappedBy
atribút @ManyToMany
anotáciu na vytvorenie tohto mapovania.
Zoznam 1 zobrazuje zdrojový kód súboru SuperHero
trieda.
Zoznam 1. SuperHero.java
balíček com.geekcap.javaworld.jpa.model; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @Entity @Table (name = "SUPER_HERO") verejná trieda SuperHero {@Id @GeneratedValue súkromné celé číslo; súkromné meno reťazca; @ManyToMany (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable (name = "SuperHero_Movies", joinColumns = {@JoinColumn (name = "superhero_id")}, inverseJoinColumns = {@JoinColumn) }) private Set films = new HashSet (); public SuperHero () {} public SuperHero (Integer id, String name) {this.id = id; this.name = meno; } public SuperHero (názov reťazca) {this.name = meno; } public Integer getId () {return id; } public void setId (integer id) {this.id = id; } public String getName () {návratové meno; } public void setName (názov reťazca) {this.name = name; } public Set getMovies () {návrat filmov; } @Override public String toString () {return "SuperHero {" + "id =" + id + ", + name +" \ '' + ", + films.stream (). Map (Movie :: getTitle) .collect (Collectors.toList ()) + "\ '' + '}'; }}
The SuperHero
trieda má niekoľko anotácií, ktoré by mali byť známe z 1. časti:
@Entity
identifikujeSuperHero
ako subjekt JPA.@ Tabuľka
mapujeSuperHero
entita do tabuľky „SUPER_HERO“.
Všimnite si tiež Celé číslo
id
pole, ktoré určuje, že sa automaticky vygeneruje primárny kľúč tabuľky.
Ďalej sa pozrieme na @ManyToMany
a @JoinTable
anotácie.
Načítavajú sa stratégie
Vec, ktorú si treba všimnúť v @ManyToMany
anotácia je spôsob, akým konfigurujeme aportovacia stratégia, ktoré môžu byť lenivé alebo nedočkavé. V tomto prípade sme nastavili aportovať
do EAGER
, aby sme pri načítaní a SuperHero
z databázy tiež automaticky načítame všetky jej príslušné Film
s.
Keby sme sa rozhodli vykonať a LENIVÝ
namiesto načítania by sme načítali iba každú z nich Film
pretože bol konkrétne prístupný. Lenivé načítanie je možné iba vtedy, keď SuperHero
je pripojený k EntityManager
; inak prístup k filmom o superhrdinovi spôsobí výnimku. Chceme mať prístup k filmom superhrdinov na požiadanie, preto v tomto prípade zvolíme možnosť EAGER
aportovacia stratégia.
CascadeType.PERSIST
Kaskádové operácie definovať, ako sa superhrdinovia a ich príslušné filmy uchovávajú do az databázy. Existuje niekoľko konfigurácií kaskádového typu, z ktorých si môžete vybrať, a o nich si povieme viac neskôr v tomto návode. Zatiaľ si všimnite, že sme nastavili kaskáda
atribút CascadeType.PERSIST
, čo znamená, že keď uložíme superhrdinu, uložia sa aj jeho filmy.
Pripojte sa k stolom
JoinTable
je trieda, ktorá uľahčuje vzájomné vzťahy medzi mnohými SuperHero
a Film
. V tejto triede definujeme tabuľku, ktorá bude ukladať primárne kľúče pre obidve SuperHero
a Film
subjekty.
Výpis 1 určuje, že bude názov tabuľky SuperHero_Movies
. The pripojiť sa k stĺpcu bude superhrdina_id
a stĺpec inverzného spojenia bude film_id
. The SuperHero
entita vlastní vzťah, takže stĺpec spojenia sa vyplní SuperHero
primárny kľúč. Stĺpec inverzného spojenia potom odkazuje na entitu na druhej strane vzťahu, čo je Film
.
Na základe týchto definícií v zozname 1 by sme očakávali vytvorenie novej tabuľky s názvom SuperHero_Movies
. Tabuľka bude mať dva stĺpce: superhrdina_id
, ktorý odkazuje na id
stĺpec SUPERHERO
stôl a film_id
, ktorý odkazuje na id
stĺpec FILM
stôl.
Trieda filmu
Zoznam 2 zobrazuje zdrojový kód súboru Film
trieda. Pripomeňme, že v obojsmernom vzťahu vlastní jeden subjekt vzťah (v tomto prípade SuperHero
), zatiaľ čo druhá je mapovaná do vzťahu. Kód v zozname 2 obsahuje mapovanie vzťahov aplikované na Film
trieda.
Zoznam 2. Movie.java
balíček com.geekcap.javaworld.jpa.model; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; import java.util.HashSet; import java.util.Set; @Entity @Table (name = "MOVIE") verejná trieda Film {@Id @GeneratedValue súkromné celé číslo; súkromný názov reťazca; @ManyToMany (mappedBy = "films", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) private Set superHeroes = new HashSet (); public Movie () {} public Movie (Integer id, String title) {this.id = id; this.title = title; } verejný film (názov reťazca) {this.title = názov; } public Integer getId () {return id; } public void setId (integer id) {this.id = id; } public String getTitle () {návratový názov; } public void setTitle (názov reťazca) {this.title = title; } public Set getSuperHeroes () {return superHeroes; } public void addSuperHero (SuperHero superHero) {superHeroes.add (superHero); superHero.getMovies (). pridať (toto); } @Override public String toString () {return "Movie {" + "id =" + id + ", + title +" \ '' + '}'; }}
Nasledujúce vlastnosti sa uplatňujú na @ManyToMany
anotácia v zozname 2:
mappedBy
odkazuje na názov poľa naSuperHero
triedy, ktorá riadi vzťah mnohých proti mnohým. V tomto prípade odkazuje na filmy pole, ktoré sme definovali v zozname 1 s príslušnýmJoinTable
.kaskáda
je nakonfigurovaný naCascadeType.PERSIST
, čo znamená, že keď aFilm
je uložený zodpovedajúciSuperHero
subjekty by sa tiež mali uložiť.aportovať
hovoríEntityManager
že by mala získať superhrdinov filmu nedočkavo: keď načíta aFilm
, mala by tiež načítať všetky zodpovedajúceSuperHero
subjekty.
Niečo iné, čo si treba uvedomiť o Film
trieda je jeho addSuperHero ()
metóda.
Pri konfigurácii entít na perzistenciu nestačí jednoducho pridať do filmu superhrdinu; musíme aktualizovať aj druhú stranu vzťahu. To znamená, že musíme film pridať k superhrdinovi. Keď sú obe strany vzťahu správne nakonfigurované, takže film má odkaz na superhrdinu a superhrdina má odkaz na film, tabuľka spojenia sa tiež správne vyplní.
Definovali sme naše dve entity. Teraz sa pozrime na úložiská, ktoré použijeme na ich perzistenciu do az databázy.
Tip! Postavte obe strany stola
Častou chybou je nastaviť iba jednu stranu vzťahu, pretrvávať v entite a potom pozorovať, že tabuľka spojení je prázdna. Toto napraví nastavenie oboch strán vzťahu.
Úložiská JPA
Celý náš kód perzistencie by sme mohli implementovať priamo vo vzorovej aplikácii, ale vytvorenie tried úložiska nám umožňuje oddeliť kód perzistencie od kódu aplikácie. Rovnako ako v prípade aplikácie Knihy a autori v 1. časti, vytvoríme aj EntityManager
a potom ho použiť na inicializáciu dvoch úložísk, jedného pre každú entitu, ktorú pretrvávame.
Zoznam 3 zobrazuje zdrojový kód súboru MovieRepository
trieda.
Zoznam 3. MovieRepository.java
balíček com.geekcap.javaworld.jpa.repository; import com.geekcap.javaworld.jpa.model.Movie; import javax.persistence.EntityManager; import java.util.List; import java.util.Voliteľné; verejná trieda MovieRepository {private EntityManager entityManager; public MovieRepository (EntityManager entityManager) {this.entityManager = entityManager; } verejné Voliteľné uloženie (filmový film) {try {entityManager.getTransaction (). begin (); entityManager.persist (film); entityManager.getTransaction (). commit (); návrat Optional.of (film); } catch (Výnimka e) {e.printStackTrace (); } návrat Optional.empty (); } public Voliteľné findById (celé číslo) {Movie movie = entityManager.find (Movie.class, id); vrátiť film! = null? Optional.of (film): Optional.empty (); } public List findAll () {return entityManager.createQuery ("from Movie"). getResultList (); } public void deleteById (Integer id) {// Načítať film s týmto ID Movie movie = entityManager.find (Movie.class, id); if (movie! = null) {try {// Spustiť transakciu, pretože zmeníme databázu entityManager.getTransaction (). begin (); // Odstrániť všetky odkazy na tento film superhrdinami movie.getSuperHeroes (). ForEach (superHero -> {superHero.getMovies (). Remove (movie);}); // Teraz odstráňte film entityManager.remove (film); // Potvrdenie transakcie entityManager.getTransaction (). Commit (); } catch (Výnimka e) {e.printStackTrace (); }}}}
The MovieRepository
sa inicializuje pomocou EntityManager
, potom ho uloží do členskej premennej, ktorá sa použije v metódach perzistencie. Zvážime každú z týchto metód.
Metódy perzistencie
Poďme preskúmať MovieRepository
metódy vytrvalosti a zistiť, ako interagujú s EntityManager
metódy vytrvalosti.