Programiranje

Vztrajnost Java z JPA in Hibernate, 2. del: Odnosi veliko do veliko

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
prenos Prenesite kodo Prenesite izvorno kodo, na primer za programe, uporabljene v tej vadnici. Ustvaril Steven Haines za JavaWorld.

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 Haines

Upoš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 identificira SuperHero kot subjekt JPA.
  • @Table zemljevidi SuperHero entiteta v tabelo "SUPER_HERO".

Upoštevajte tudi Celo številoid 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 Films.

Č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 SuperHeroje 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 na SuperHero razred, ki upravlja razmerje med mnogimi in mnogimi. V tem primeru se sklicuje na filmi polje, ki smo ga na seznamu 1 opredelili z ustreznim JoinTable.
  • kaskada je nastavljen na CascadeType.PERSIST, kar pomeni, da kadar a Film je shranjena njegova ustrezna SuperHero prav tako je treba shraniti entitete.
  • prinesi pove EntityManager da bi moral pridobiti superjunake filma nestrpno: ko naloži a Film, naložil naj bi tudi vse ustrezne SuperHero 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 MovieRepositorymetode vztrajnosti in si oglejte, kako vplivajo na EntityManagermetode vztrajnosti.

$config[zx-auto] not found$config[zx-overlay] not found