Ohjelmointi

Java-pysyvyys JPA: n ja horrostilan kanssa, osa 2: Monista moniin-suhteet

Tämän opetusohjelman alkupuoliskolla esiteltiin Java Persistence -sovellusliittymän perusteet ja näytettiin, kuinka voit määrittää JPA-sovelluksen horrostilan 5.3.6 ja Java 8: n avulla. Jos olet lukenut kyseisen opetusohjelman ja tutkinut sen esimerkkisovellusta, tiedät JPA-entiteettien ja monenvälisten suhteiden mallintaminen JPA: ssa. Sinulla on myös ollut jonkin verran käytäntöä nimettyjen kyselyiden kirjoittamiseen JPA Query Language (JPQL) -kielellä.

Tässä opetusohjelman toisessa puoliskossa menemme syvemmälle JPA: n ja horrostilan kanssa. Opit mallinnamaan monien ja monien välisen suhteen Elokuva ja SuperHero entiteetit, perustaa yksittäisiä arkistoja näille entiteeteille ja ylläpitää entiteettejä H2-muistitietokantaan. Opit myös enemmän kaskadioperaatioiden roolista JPA: ssa ja saat vinkkejä a CascadeType strategia tietokannassa oleville yksiköille. Lopuksi kootaan yhteen toimiva sovellus, jonka voit suorittaa IDE: ssä tai komentorivillä.

Tämä opetusohjelma keskittyy JPA: n perusteisiin, mutta muista tarkistaa nämä Java-vinkit, joissa esitellään edistyneempiä aiheita JPA: ssa:

  • Perintösuhteet JPA: ssa ja horrostilassa
  • Yhdistetyt avaimet JPA: ssa ja horrostilassa
lataa Hae koodi Lataa lähdekoodi esimerkiksi tässä opetusohjelmassa käytetyistä sovelluksista. Luonut Steven Haines JavaWorldille.

JPA: n monen ja monen väliset suhteet

Monista moniin-suhteet määritellä entiteetit, joiden suhteen molemmilla puolilla voi olla useita viittauksia toisiinsa. Esimerkkinä aiomme mallintaa elokuvia ja supersankareita. Toisin kuin osan 1 kirjoittajat ja kirjat, elokuvassa voi olla useita supersankareita, ja supersankari voi esiintyä useissa elokuvissa. Supersankarimme, Ironman ja Thor, esiintyvät molemmissa elokuvissa, "Kostajat" ja "Kostajat: Infinity War".

Tämän monien ja monien välisen suhteen mallintamiseksi JPA: lla tarvitaan kolme taulukkoa:

  • ELOKUVA
  • SUPER_HERO
  • SUPERHERO_MOVIES

Kuvassa 1 on esitetty verkkotunnusmalli kolmen taulukon kanssa.

Steven Haines

Ota huomioon, että SuperHero_Movies on liittyä pöydään välissä Elokuva ja SuperHero taulukoita. JPA: ssa liittymispöytä on erityinen pöytä, joka helpottaa monien ja monien välistä suhdetta.

Yksisuuntainen vai kaksisuuntainen?

JPA: ssa käytämme @ManyToMany merkintä monien ja monien välisten suhteiden mallintamiseksi. Tämän tyyppinen suhde voi olla yksisuuntainen tai kaksisuuntainen:

  • Jonkin sisällä yksisuuntainen suhde vain yksi suhteessa oleva yksikkö osoittaa toista.
  • Jonkin sisällä kaksisuuntainen suhde molemmat entiteetit osoittavat toisiaan.

Esimerkkimme on kaksisuuntainen, eli elokuva osoittaa kaikkiin supersankareihinsa ja supersankari osoittaa kaikkiin heidän elokuviinsa. Kaksisuuntaisessa, monista moniin -suhteessa yksi kokonaisuus omistaa suhde ja toinen on kartoitettu suhde. Käytämme kartoitettu attribuutti @ManyToMany merkintä tämän kartoituksen luomiseksi.

Luettelossa 1 näkyy SuperHero luokassa.

Listaus 1. SuperHero.java

 paketti com.geekcap.javaworld.jpa.model; tuo javax.persistence.CascadeType; tuo javax.persistence.Entity; tuo javax.persistence.FetchType; tuo javax.persistence.GeneratedValue; tuo javax.persistence.Id; tuo javax.persistence.JoinColumn; tuo javax.persistence.JoinTable; tuo javax.persistence.ManyToMany; tuo javax.persistence.Table; tuo java.util.HashSet; tuo java.util.Set; tuoda java.util.stream.Collectors; @Entity @Table (name = "SUPER_HERO") julkisen luokan SuperHero {@Id @GeneratedValue private Integer id; yksityinen merkkijono nimi; @ManyToMany (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable (name = "SuperHero_Movies", joinColumns = {@JoinColumn (name = "superhero_id")}, inverseJoinColumns = {@JoinColumn (name =) }) yksityisryhmäelokuvat = uusi HashSet (); public SuperHero () {} public SuperHero (Integer id, String name) {this.id = id; tämä.nimi = nimi; } public SuperHero (String name) {tämä.nimi = nimi; } public Integer getId () {return ID; } public void setId (kokonaisluku) {this.id = id; } public String getName () {palautusnimi; } public void setName (Merkkijonon nimi) {this.name = nimi; } public set getMovies () {paluuelokuvat; } @Override public String toString () {return "SuperHero {" + "id =" + id + ", + name +" \ '' + ", + movies.stream (). Map (Movie :: getTitle) .collect (Collectors.toList ()) + "\ '' + '}'; }} 

SuperHero luokassa on pari merkintää, joiden tulisi olla tuttuja osasta 1:

  • @Entity tunnistaa SuperHero JPA-kokonaisuutena.
  • @Pöytä kartoittaa SuperHero "SUPER_HERO" -taulukkoon.

Huomaa myös Kokonaislukuid kenttä, joka määrittää, että taulukon ensisijainen avain luodaan automaattisesti.

Seuraavaksi tarkastelemme @ManyToMany ja @JoinTable merkinnät.

Strategioiden hakeminen

Asia, joka on huomioitava @ManyToMany huomautus on miten konfiguroimme noutostrategia, joka voi olla laiska tai innokas. Tässä tapauksessa olemme asettaneet noutaa että INNOKAS, niin että kun haemme a SuperHero tietokannasta haemme myös automaattisesti kaikki vastaavat Elokuvas.

Jos päätämme suorittaa a LAISKA sen sijaan haemme vain kukin Elokuva koska sitä oli erityisesti käytetty. Laiska nouto on mahdollista vain SuperHero on kiinnitetty EntityManager; muuten supersankarin elokuvien katselu aiheuttaa poikkeuksen. Haluamme pystyä katsomaan supersankarin elokuvia pyynnöstä, joten tässä tapauksessa valitsemme INNOKAS noutostrategia.

CascadeType.PERSIST

Cascade-toiminnot määritellä, miten supersankareita ja niitä vastaavia elokuvia säilytetään tietokannassa ja siitä. Valittavana on useita kaskadityyppisiä kokoonpanoja, joista puhumme myöhemmin tässä opetusohjelmassa. Toistaiseksi huomaa vain, että olemme asettaneet ryöpytä attribuutti CascadeType.PERSIST, mikä tarkoittaa, että kun tallennamme supersankarin, myös sen elokuvat tallennetaan.

Liity pöytiin

JoinTable on luokka, joka helpottaa monien ja monien välistä suhdetta SuperHero ja Elokuva. Tässä luokassa määritellään taulukko, joka tallentaa molempien pääavaimet SuperHero ja Elokuva yhteisöt.

Listaus 1 määrittää, että taulukon nimi tulee olemaan SuperHero_Movies. liity sarakkeeseen tulee olemaan supersankari_id, ja käänteinen liitos sarake tulee olemaan movie_id. SuperHero entiteetti omistaa suhteen, joten liittymissarake täytetään SuperHeroensisijainen avain. Käänteisliitos -sarakkeessa viitataan sitten entiteettiin suhteen toisella puolella, mikä on Elokuva.

Näiden luettelon 1 määritelmien perusteella odotamme uuden taulukon luomista nimeltä SuperHero_Movies. Taulukossa on kaksi saraketta: supersankari_id, joka viittaa id -sarakkeessa SUPERHERO ja taulukko movie_id, joka viittaa id -sarakkeessa ELOKUVA pöytä.

Elokuva-luokka

Luettelossa 2 näkyy Elokuva luokassa. Muistakaamme, että kaksisuuntaisessa suhteessa yksi yksikkö omistaa suhteen (tässä tapauksessa SuperHero), kun taas toinen on kartoitettu suhteeseen. Listing 2: n koodi sisältää Elokuva luokassa.

Listaus 2. Movie.java

 paketti com.geekcap.javaworld.jpa.model; tuo javax.persistence.CascadeType; tuo javax.persistence.Entity; tuo javax.persistence.FetchType; tuo javax.persistence.GeneratedValue; tuo javax.persistence.Id; tuo javax.persistence.ManyToMany; tuo javax.persistence.Table; tuo java.util.HashSet; tuo java.util.Set; @Entity @Table (name = "MOVIE") public class Movie {@Id @GeneratedValue private Integer id; yksityinen merkkijono; @ManyToMany (mappedBy = "elokuvat", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) yksityisjoukko superHeroes = uusi HashSet (); julkinen elokuva () {} julkinen elokuva (kokonaisluku, merkkijono) {this.id = id; this.title = otsikko; } julkinen elokuva (merkkijono) {this.title = title; } public Integer getId () {return ID; } public void setId (kokonaisluku) {this.id = id; } public String getTitle () {return title; } public void setTitle (Merkkijono) {this.title = title; } public set getSuperHeroes () {return superHeroes; } public void addSuperHero (SuperHero superHero) {superHeroes.add (superHero); superHero.getMovies (). lisää (tämä); } @Override public String toString () {return "Elokuva {" + "id =" + id + ", + title +" \ '' + '}'; }}

Seuraavia ominaisuuksia käytetään @ManyToMany merkintä luettelossa 2:

  • kartoitettu viittaa kentän nimeen SuperHero luokka, joka hoitaa monia moniin-suhdetta. Tässä tapauksessa se viittaa elokuvia , jonka määrittelemme luettelossa 1 vastaavalla JoinTable.
  • ryöpytä on määritetty CascadeType.PERSIST, mikä tarkoittaa, että kun a Elokuva tallennetaan vastaava SuperHero myös yksiköt tulisi tallentaa.
  • hae kertoo EntityManager että sen pitäisi hakea elokuvan supersankareita innokkaasti: kun se latautuu a Elokuva, sen tulisi myös ladata kaikki vastaavat SuperHero yhteisöt.

Jotain muuta huomattavaa Elokuva luokka on sen addSuperHero () menetelmä.

Kun määrität entiteettejä pysyvyyttä varten, ei riitä, että supersankari lisätään yksinkertaisesti elokuvaan; meidän on myös päivitettävä suhteiden toinen puoli. Tämä tarkoittaa, että meidän on lisättävä elokuva supersankariin. Kun suhteen molemmat puolet on määritetty oikein, niin että elokuvassa on viittaus supersankariin ja supersankariin viittaus elokuvaan, myös liitostaulukko täytetään oikein.

Olemme määrittäneet kaksi yksikköämme. Tarkastellaan nyt arkistoja, joita käytämme niiden säilyttämiseen tietokantaan ja siitä.

Kärki! Aseta pöydän molemmat puolet

Se on yleinen virhe asettaa vain yksi puoli suhteesta, säilyttää entiteetti ja havaita sitten, että liittymispöytä on tyhjä. Suhteen molempien puolien asettaminen korjaa tämän.

JPA-arkistot

Voisimme toteuttaa kaiken pysyvyyskoodimme suoraan esimerkkisovelluksessa, mutta arkistoluokkien luominen antaa meille mahdollisuuden erottaa pysyvyyskoodi sovelluskoodista. Aivan kuten teimme osassa 1 olevan Kirjat ja kirjoittajat -sovelluksen kanssa, luomme EntityManager ja käytä sitä sen jälkeen kahden alustuksen alustamiseen, yksi kutakin jatkuvaa yksikköä varten.

Luettelossa 3 näkyy MovieRepository luokassa.

Listaus 3. MovieRepository.java

 paketti com.geekcap.javaworld.jpa.repository; tuo com.geekcap.javaworld.jpa.model.Elokuva; tuo javax.persistence.EntityManager; tuo java.util.List; tuo java.util.Valinnainen; julkinen luokka MovieRepository {private EntityManager entityManager; public MovieRepository (EntityManager entityManager) {this.entityManager = entitManager; } julkinen Valinnainen tallennus (elokuva) {kokeile {entityManager.getTransaction (). begin (); entitManager.persist (elokuva); entityManager.getTransaction (). sitoutu (); return Valinnainen (elokuva); } catch (Poikkeus e) {e.printStackTrace (); } return Valinnainen. tyhjä (); } public Valinnainen findById (Kokonaisluku) {Elokuvaelokuva = entitManager.find (Elokuva.luokka, id); palaa elokuvaan! = null? Valinnainen. (Elokuva): Valinnainen. Tyhjä (); } public list findAll () {return entityManager.createQuery ("from Movie"). getResultList (); } public void deleteById (Kokonaisluku) {// Nouda elokuva tällä tunnuksella Elokuva elokuva = entitManager.find (Elokuva.luokka, id); if (elokuva! = null) {yritä {// Aloita tapahtuma, koska muutamme tietokannan entiteettiäManager.getTransaction (). begin (); // Poista kaikki viittaukset tähän elokuvaan supersankareiden kautta movie.getSuperHeroes (). ForEach (superHero -> {superHero.getMovies (). Remove (movie);}); // Poista nyt elokuvayksikköManager.remove (movie); // Suorita tapahtuman entiteettiManager.getTransaction (). Sitoutu (); } catch (Poikkeus e) {e.printStackTrace (); }}}} 

MovieRepository on alustettu EntityManager, sitten tallentaa sen jäsenmuuttujaan käytettäväksi sen pysyvyysmenetelmissä. Harkitsemme kaikkia näitä menetelmiä.

Pysyvyysmenetelmät

Tarkastellaan MovieRepositoryn pysyvyysmenetelmiä ja miten ne ovat vuorovaikutuksessa EntityManagersinnikkyysmenetelmät.