Prva polovica te vadnice je predstavila osnove API za obstojnost Java in vam pokazala, kako konfigurirati aplikacijo JPA s pomočjo Hibernate 5.3.6 in Java 8. Če ste prebrali to vadnico in preučevali njen primer uporabe, potem poznate osnove modeliranje JPA entitet in medsebojnih odnosov v JPA. Imeli ste tudi nekaj vaj za pisanje poimenovanih poizvedb z jezikom poizvedb JPA (JPQL).
V tej drugi polovici vadnice bomo poglobili JPA in Hibernate. Naučili se boste, kako oblikovati razmerje med mnogimi in mnogimi Film
in SuperHero
entitete, za te entitete nastavi posamezne repozitorije in vztraja v entitetah v podatkovni bazi H2. Izvedeli boste tudi več o vlogi kaskadnih operacij v JPA in dobili nasvete za izbiro a CascadeType
strategija za entitete v bazi podatkov. Na koncu bomo sestavili delujočo aplikacijo, ki jo lahko zaženete v vašem IDE ali v ukazni vrstici.
Ta vadnica se osredotoča na osnove JPA, vendar si oglejte te nasvete za Java, ki uvajajo naprednejše teme v JPA:
- Dedna razmerja v JPA in hibernaciji
- Sestavljeni ključi v JPA in Hibernate
Odnosi veliko do veliko v JPA
Odnosi veliko do veliko definirajte entitete, pri katerih se lahko obe strani odnosa večkrat sklicujeta druga na drugo. Za naš primer bomo modelirali filme in superjunake. Za razliko od primera Avtorji in knjige iz 1. dela ima film lahko več superjunakov, superjunak pa se lahko pojavi v več filmih. Naši superjunaki, Ironman in Thor, se pojavita v dveh filmih, "Maščevalci" in "Maščevalci: Vojna neskončnosti".
Za modeliranje tega razmerja med mnogimi in mnogimi z uporabo JPA bomo potrebovali tri tabele:
- FILM
- SUPER_HERO
- SUPERHERO_MOVIES
Slika 1 prikazuje model domene s tremi tabelami.
Steven HainesUpoštevajte to SuperHero_Movies
je pridruži mizo med Film
in SuperHero
mize. V JPA je združevalna tabela posebna vrsta tabele, ki olajša odnos med mnogimi in mnogimi.
Enosmerna ali dvosmerna?
V JPA uporabljamo @ManyToMany
pripomba za modeliranje odnosov veliko do veliko. Ta vrsta odnosa je lahko enosmerna ali dvosmerna:
- V enosmerno razmerje samo ena entiteta v razmerju kaže drugo.
- V dvosmerno razmerje obe entiteti kažeta drug na drugega.
Naš primer je dvosmeren, kar pomeni, da film kaže na vse svoje superjunake, superheroj pa na vse njihove filme. V dvosmernem razmerju mnogo proti mnogim ena entiteta ima v lasti razmerje in drugo je preslikana na razmerje. Uporabljamo mappedBy
atribut @ManyToMany
za ustvarjanje tega preslikavanja.
Seznam 1 prikazuje izvorno kodo za SuperHero
razred.
Seznam 1. SuperHero.java
paket com.geekcap.javaworld.jpa.model; uvoz javax.persistence.CascadeType; uvoz javax.persistence.Entity; uvoz javax.persistence.FetchType; uvoz javax.persistence.GeneratedValue; uvoz javax.persistence.Id; uvoz javax.persistence.JoinColumn; import javax.persistence.JoinTable; uvoz javax.persistence.ManyToMany; uvoz javax.persistence.Table; uvoz java.util.HashSet; import java.util.Set; uvoz java.util.stream.Collectors; @Entity @Table (name = "SUPER_HERO") javni razred SuperHero {@Id @GeneratedValue private Integer id; ime zasebnega niza; @ManyToMany (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable (name = "SuperHero_Movies", joinColumns = {@JoinColumn (name = "superhero_id")}, inverseJoinColumns = {@JoinColumn }) zasebni Set filmov = nov HashSet (); javni SuperHero () {} javni SuperHero (celoštevilčni id, ime niza) {this.id = id; this.name = ime; } javni SuperHero (ime niza) {this.name = name; } public Integer getId () {return id; } javna void setId (celoštevilčni id) {this.id = id; } javni niz getName () {return ime; } javna void setName (ime niza) {this.name = name; } javni Set getMovies () {vrnitev filmov; } @Override public String toString () {return "SuperHero {" + "id =" + id + ", + name +" \ '' + ", + movies.stream (). Map (Movie :: getTitle) .collect (Collectors.toList ()) + "\ '' + '}'; }}
The SuperHero
class vsebuje nekaj pripisov, ki bi jih morali poznati iz 1. dela:
@Entiteta
identificiraSuperHero
kot subjekt JPA.@Table
zemljevidiSuperHero
entiteta v tabelo "SUPER_HERO".
Upoštevajte tudi Celo število
id
polje, ki določa, da se bo primarni ključ tabele samodejno ustvaril.
Nato si bomo ogledali @ManyToMany
in @JoinTable
opombe.
Strategije pridobivanja
Stvar, ki jo je treba opaziti v @ManyToMany
pripis je, kako konfiguriramo strategija pridobivanja, ki je lahko len ali nestrpen. V tem primeru smo nastavili prinesi
do ZAVZET
, tako da, ko pridobimo a SuperHero
iz baze podatkov bomo tudi samodejno pridobili vse ustrezne Film
s.
Če smo se odločili za izvedbo a LENJEN
namesto tega bi dobili samo vsakega Film
kot je bilo posebej dostopno. Leno pridobivanje je možno le, medtem ko SuperHero
je pritrjen na EntityManager
; v nasprotnem primeru bo dostop do filmov o superjunakih izjema. Želimo imeti dostop do filmov o superherojih na zahtevo, zato v tem primeru izberemo ZAVZET
strategija pridobivanja.
CascadeType.PERSIST
Kaskadne operacije določite, kako se superjunaki in njihovi ustrezni filmi ohranijo v zbirki podatkov in iz nje. Izbirate lahko med številnimi konfiguracijami kaskadnih vrst, o katerih bomo več govorili kasneje v tej vadnici. Zaenkrat upoštevajte, da smo nastavili kaskada
atribut CascadeType.PERSIST
, kar pomeni, da bodo, ko shranimo superjunaka, shranjeni tudi njegovi filmi.
Združi tabele
JoinTable
je razred, ki olajša odnos med mnogimi SuperHero
in Film
. V tem razredu določimo tabelo, v kateri bodo shranjeni primarni ključi obeh SuperHero
in Film
subjekti.
Seznam 1 določa, da bo ime tabele SuperHero_Movies
. The pridruži stolpec bo superhero_id
, in inverzni stolpec združevanja bo movie_id
. The SuperHero
entiteta je lastnik razmerja, zato bo stolpec za združevanje izpolnjen z SuperHero
je glavni ključ. Stolpec inverzne združitve se nato sklicuje na entiteto na drugi strani odnosa, kar je Film
.
Na podlagi teh opredelitev v seznamu 1 bi pričakovali novo tabelo z imenom SuperHero_Movies
. Tabela bo imela dva stolpca: superhero_id
, ki se sklicuje na id
stolpec SUPERHEROJ
tabela in movie_id
, ki se sklicuje na id
stolpec FILM
tabela.
Predavanje filma
Seznam 2 prikazuje izvorno kodo za Film
razred. Spomnimo se, da je v dvosmernem razmerju razmerje v lasti enega subjekta (v tem primeru SuperHero
), medtem ko je drugi preslikan v razmerje. Koda v seznamu 2 vključuje preslikavo odnosov, ki se uporablja za Film
razred.
Seznam 2. Movie.java
paket com.geekcap.javaworld.jpa.model; uvoz javax.persistence.CascadeType; uvoz javax.persistence.Entity; uvoz javax.persistence.FetchType; uvoz javax.persistence.GeneratedValue; uvoz javax.persistence.Id; uvoz javax.persistence.ManyToMany; uvoz javax.persistence.Table; uvoz java.util.HashSet; import java.util.Set; @Entity @Table (name = "MOVIE") javni razred filma {@Id @GeneratedValue private Integer id; naslov zasebnega niza; @ManyToMany (mappedBy = "movies", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) private Set superHeroes = new HashSet (); javni film () {} javni film (celoštevilčni id, naslov niza) {this.id = id; this.title = naslov; } javni film (naslov niza) {this.title = naslov; } public Integer getId () {return id; } javna void setId (celoštevilčni id) {this.id = id; } javni String getTitle () {return naslov; } javna void setTitle (naslov niza) {this.title = title; } public Set getSuperHeroes () {return superHeroes; } javna void addSuperHero (SuperHero superHero) {superHeroes.add (superHero); superHero.getMovies (). add (this); } @Override public String toString () {return "Movie {" + "id =" + id + ", + title +" \ '' + '}'; }}
Naslednje lastnosti veljajo za @ManyToMany
pripis v seznamu 2:
mappedBy
se sklicuje na ime polja naSuperHero
razred, ki upravlja razmerje med mnogimi in mnogimi. V tem primeru se sklicuje na filmi polje, ki smo ga na seznamu 1 opredelili z ustreznimJoinTable
.kaskada
je nastavljen naCascadeType.PERSIST
, kar pomeni, da kadar aFilm
je shranjena njegova ustreznaSuperHero
prav tako je treba shraniti entitete.prinesi
poveEntityManager
da bi moral pridobiti superjunake filma nestrpno: ko naloži aFilm
, naložil naj bi tudi vse ustrezneSuperHero
subjekti.
Še nekaj, kar je treba opozoriti na Film
razred je svoj addSuperHero ()
metoda.
Ko konfigurirate entitete za vztrajnost, ni dovolj, da v film preprosto dodate superheroja; posodobiti moramo tudi drugo plat odnosa. To pomeni, da moramo film dodati superjunaku. Ko sta obe strani razmerja pravilno konfigurirani, tako da ima film sklic na superjunaka, superjunak pa na film, bo tudi tabela združevanja pravilno izpolnjena.
Določili smo dve entiteti. Zdaj pa poglejmo skladišča, s katerimi jih bomo obdržali v zbirki podatkov in iz nje.
Namig! Postavite obe strani mize
Pogosta napaka je, da nastavimo samo eno stran odnosa, vztrajamo v entiteti in nato opazimo, da je tabela združevanja prazna. Nastavitev obeh strani odnosa bo to popravila.
Repozitoriji JPA
Vso kodo za obstojnost bi lahko implementirali neposredno v vzorčni aplikaciji, toda ustvarjanje razredov repozitorija nam omogoča ločevanje kode za obstojnost od kode aplikacije. Tako kot smo to storili z aplikacijo Knjige in avtorji v 1. delu, bomo tudi mi ustvarili EntityManager
in nato z njim inicializirajte dve repozitoriji, po eno za vsako entiteto, ki jo vztrajamo.
Seznam 3 prikazuje izvorno kodo za MovieRepository
razred.
Seznam 3. MovieRepository.java
paket com.geekcap.javaworld.jpa.repository; uvoz com.geekcap.javaworld.jpa.model.Movie; uvoz javax.persistence.EntityManager; uvoz java.util.List; uvoz java.util.Neobvezno; javni razred MovieRepository {private EntityManager entityManager; javni MovieRepository (EntityManager entityManager) {this.entityManager = entityManager; } javno Neobvezno shranjevanje (filmski film) {poskusite {entityManager.getTransaction (). begin (); entityManager.persist (film); entityManager.getTransaction (). commit (); vrnitev Neobvezno.of (film); } catch (izjema e) {e.printStackTrace (); } return Neobvezno.empty (); } public Neobvezno findById (celoštevilčni id) {Movie movie = entityManager.find (Movie.class, id); vrni film! = null? Neobvezno.of (film): Neobvezno.prazno (); } javni seznam findAll () {return entityManager.createQuery ("iz filma"). getResultList (); } public void deleteById (Integer id) {// Pridobi film s tem ID Movie movie = entityManager.find (Movie.class, id); if (movie! = null) {try {// Zaženite transakcijo, ker bomo spremenili entiteto baze podatkovManager.getTransaction (). begin (); // Odstrani vse reference tega filma s strani superjunakov movie.getSuperHeroes (). ForEach (superHero -> {superHero.getMovies (). Remove (movie);}); // Zdaj odstranite film entityManager.remove (film); // Zavežite transakcijo entityManager.getTransaction (). Commit (); } catch (izjema e) {e.printStackTrace (); }}}}
The MovieRepository
je inicializiran z EntityManager
, nato jo shrani v spremenljivko člana, ki jo bo uporabila v svojih metodah trajanja. Upoštevali bomo vsako od teh metod.
Metode obstojnosti
Oglejmo si MovieRepository
metode vztrajnosti in si oglejte, kako vplivajo na EntityManager
metode vztrajnosti.