Pirmoje šios pamokos pusėje buvo pristatyti pagrindiniai „Java Persistence“ API pagrindai ir parodyta, kaip sukonfigūruoti JPA programą naudojant „Hibernate 5.3.6“ ir „Java 8“. Jei perskaitėte tą pamoką ir išstudijavote jos pavyzdinę programą, žinote pagrindinius modeliuojant JPA subjektus ir daugybės santykius JPA. Jūs taip pat turėjote praktikos rašyti pavadintas užklausas naudojant JPA užklausų kalbą (JPQL).
Šioje antrojoje mokymo programos pusėje gilinsimės į JPA ir „Hibernate“. Sužinosite, kaip modeliuoti santykį „visi daugeliui“ Filmas
ir Super herojus
subjektai, sukurkite atskiras šių objektų saugyklas ir išsaugokite objektus H2 atminties duomenų bazėje. Taip pat sužinosite daugiau apie kaskados operacijų vaidmenį JPA ir gausite patarimų, kaip pasirinkti „CascadeType“
strategija subjektams duomenų bazėje. Galiausiai sukursime veikiančią programą, kurią galėsite paleisti savo IDE arba komandinėje eilutėje.
Šioje pamokoje daugiausia dėmesio skiriama JPA pagrindams, tačiau būtinai peržiūrėkite šiuos „Java“ patarimus, kuriuose pateikiamos pažangesnės JPA temos:
- Paveldėjimo santykiai JPA ir hibernate
- Sudėtiniai JPA ir užmigdymo raktai
JPA santykiai „daugeliui į daugelį“
Santykiai nuo daugelio iki daugelio apibrėžti subjektus, kuriems abi santykio pusės gali turėti keletą nuorodų vienas į kitą. Pavyzdžiui, ketiname kurti filmus ir superherojus. Skirtingai nuo Autorių ir knygų pavyzdžio iš 1 dalies, filme gali būti keli superherojai, o superherojus gali pasirodyti keliuose filmuose. Mūsų superherojai - Ironmanas ir Thoras - pasirodo dviejuose filmuose: „Keršytojai“ ir „Keršytojai: begalybės karas“.
Norint modeliuoti šį daugeliui santykių naudojant JPA, mums reikės trijų lentelių:
- FILMAS
- SUPER HEROJUS
- SUPERHERO_MOVIES
1 paveiksle pateiktas domeno modelis su trimis lentelėmis.

Prisimink tai „SuperHero_Movies“
yra prisijungti prie stalo tarp Filmas
ir Super herojus
stalai. JPA prisijungimo stalas yra ypatinga stalo rūšis, palengvinanti santykius „visi daugeliui“.
Vienakryptis ar dvikryptis?
JPA mes naudojame @ManyToMany
anotacija modeliuoti daugeliui santykių. Šio tipo santykiai gali būti vienkryptiai arba dvikryptiai:
- A vienakryptis santykis tik vienas santykio subjektas nurodo kitą.
- A dvikryptis santykis abu subjektai rodo vienas į kitą.
Mūsų pavyzdys yra dvikryptis, tai reiškia, kad filmas rodo visus savo superherojus, o superherojus - į visus jų filmus. Dviejų krypčių santykiuose „vienas prieš daugelį“ vienas subjektas priklauso santykiai, o kitas yra susieti su santykiai. Mes naudojame atvaizduota
atributas @ManyToMany
anotacija sukurti šį žemėlapį.
1 sąraše rodomas šaltinio kodas Super herojus
klasė.
Sąrašas 1. „SuperHero.java“
paketas com.geekcap.javaworld.jpa.model; importuoti javax.persistence.CascadeType; importuoti javax.persistence.Entity; importuoti javax.persistence.FetchType; importuoti javax.persistence.GeneratedValue; importuoti javax.persistence.Id; importuoti javax.persistence.JoinColumn; importuoti javax.persistence.JoinTable; importuoti javax.persistence.ManyToMany; importuoti javax.persistence.Table; importuoti java.util.HashSet; importuoti java.util.Set; importuoti java.util.stream.Collectors; @Entity @Table (name = "SUPER_HERO") viešosios klasės „SuperHero“ {@Id @GeneratedValue private Integer ID; asmeninės eilutės pavadinimas; @ManyToMany (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable (name = "SuperHero_Movies", joinColumns = {@JoinColumn (name = "superhero_id")}}, inverseJoinColumns = {@JoinColumn (name = }) privatūs filmai = naujas „HashSet“ (); public SuperHero () {} public SuperHero (sveikasis skaičius, eilutės pavadinimas) {this.id = id; this.name = vardas; } public super herojus (eilutės pavadinimas) {this.name = vardas; } public Integer getId () {return id; } public void setId (sveikasis skaičius) {this.id = id; } public String getName () {grąžinimo vardas; } public void setName (eilutės pavadinimas) {this.name = vardas; } public set getMovies () {return filmai; } @Paisyti viešosios eilutės toString () {grąžinti „SuperHero“ {"+" id = "+ id +", + vardas + "\ '' +", + movies.stream (). Žemėlapis (filmas :: getTitle) .collect (Collectors.toList ()) + "\ '' + '}'; }}
Super herojus
klasėje yra keletas komentarų, kurie turėtų būti žinomi iš 1 dalies:
@Entity
identifikuojaSuper herojus
kaip JPA subjektas.@ Lentelė
žemėlapiaiSuper herojus
„SUPER_HERO“ lentelėje.
Taip pat atkreipkite dėmesį į Sveikasis skaičius
id
laukas, nurodantis, kad lentelės pagrindinis raktas bus automatiškai sugeneruotas.
Toliau mes pažvelgsime į @ManyToMany
ir @JoinTable
anotacijos.
Gaunamos strategijos
Dalykas, kurį reikia pastebėti @ManyToMany
komentaras yra tai, kaip mes konfigūruojame gavimo strategija, kuris gali būti tingus ar trokštantis. Šiuo atveju mes nustatėme atnešti
į EAGER
, taigi, kai gausime a Super herojus
iš duomenų bazės mes taip pat automatiškai nuskaitysime visus atitinkamus Filmas
s.
Jei pasirinktume atlikti a Tingus
vietoj to, mes gautume tik kiekvieną Filmas
nes buvo specialiai prieinama. Tinginys yra įmanomas tik tada, kai Super herojus
yra pritvirtintas prie „EntityManager“
; kitaip žiūrint į superherojaus filmus bus išimtis. Mes norime, kad pagal poreikį galėtume patekti į superherojaus filmus, todėl šiuo atveju mes pasirenkame EAGER
gavimo strategija.
„CascadeType.PERSIST“
Kaskados operacijos apibrėžti, kaip superherojai ir atitinkami jų filmai išlaikomi duomenų bazėje ir iš jos. Galima rinktis iš kelių kaskados tipo konfigūracijų, apie kurias daugiau pakalbėsime vėliau šioje pamokoje. Kol kas tiesiog atkreipkite dėmesį, kad mes nustatėme kaskada
priskirti „CascadeType.PERSIST“
, o tai reiškia, kad kai išsaugosime superherojų, bus išsaugoti ir jo filmai.
Sujunkite lenteles
„JoinTable“
yra klasė, kuri palengvina daugelio daugeliui santykius Super herojus
ir Filmas
. Šioje klasėje mes apibrėžiame lentelę, kurioje bus saugomi pagrindiniai raktai Super herojus
ir Filmas
subjektai.
1 sąraše nurodoma, kad lentelės pavadinimas bus „SuperHero_Movies“
. prisijungti prie stulpelio bus superherojus_id
, ir atvirkštinio sujungimo stulpelis bus filmo ID
. Super herojus
subjektas turi santykius, todėl bus užpildytas prisijungimo stulpelis Super herojus
pagrindinis raktas. Tada atvirkštinio sujungimo stulpelis nurodo esmę kitoje santykio pusėje, kuri yra Filmas
.
Remdamiesi šiais apibrėžimais 1 sąraše, tikėtumės, kad bus sukurta nauja lentelė, pavadinta „SuperHero_Movies“
. Lentelėje bus du stulpeliai: superherojus_id
, kuris nurodo id
stulpelis SUPER HEROJUS
stalas ir filmo ID
, kuris nurodo id
stulpelis FILMAS
stalo.
Kino klasė
2 sąraše rodomas šaltinio kodas Filmas
klasė. Primename, kad dvikrypčiuose santykiuose vienas subjektas turi santykius (šiuo atveju Super herojus
), o kitas priskirtas santykiams. 2 sąrašo kodas apima ryšį, taikomą Filmas
klasė.
Sąrašas 2. Movie.java
paketas com.geekcap.javaworld.jpa.model; importuoti javax.persistence.CascadeType; importuoti javax.persistence.Entity; importuoti javax.persistence.FetchType; importuoti javax.persistence.GeneratedValue; importuoti javax.persistence.Id; importuoti javax.persistence.ManyToMany; importuoti javax.persistence.Table; importuoti java.util.HashSet; importuoti java.util.Set; @Entity @Table (name = "MOVIE") viešosios klasės filmas {@Id @GeneratedValue private Integer id; privačios stygos pavadinimas; @ManyToMany (mappedBy = "filmai", kaskada = CascadeType.PERSIST, fetch = FetchType.EAGER) privatus rinkinys super herojai = naujas HashSet (); viešasis filmas () {} viešasis filmas (sveikasis skaičius, eilutės pavadinimas) {this.id = id; this.title = pavadinimas; } viešasis filmas (eilutės pavadinimas) {this.title = title; } public Integer getId () {return id; } public void setId (sveikasis skaičius) {this.id = id; } public String getTitle () {return title; } public void setTitle (eilutės pavadinimas) {this.title = pavadinimas; } public set getSuperHeroes () {return superHeroes; } public void addSuperHero („SuperHero superHero“) {superHeroes.add (superHero); superHero.getMovies (). pridėti (tai); } @Paisyti viešos eilutės toString () {grąžinti "Filmas {" + "id =" + id + ", + pavadinimas +" \ '' + '}'; }}
Toliau nurodytos savybės taikomos @ManyToMany
2 sąrašo anotacija:
atvaizduota
nuoroda į lauko pavadinimąSuper herojus
klasė, kuri valdo santykius daugeliui į daugelį. Šiuo atveju jis nurodo filmai lauką, kurį mes apibrėžėme 1 sąraše su atitinkamu„JoinTable“
.kaskada
yra sukonfigūruotas„CascadeType.PERSIST“
, o tai reiškia, kad kai aFilmas
yra išsaugotas atitinkamasSuper herojus
subjektai taip pat turėtų būti išsaugoti.atnešti
pasakoja„EntityManager“
kad jis turėtų atgauti filmo superherojus noriai: kai jis pakraunamas aFilmas
, jis taip pat turėtų įkelti visus atitinkamusSuper herojus
subjektai.
Kažkas dar reikėtų atkreipti dėmesį į Filmas
klasė yra jos addSuperHero ()
metodas.
Konfigūruojant subjektų atkaklumą nepakanka tiesiog pridėti superherojų prie filmo; taip pat turime atnaujinti kitą santykių pusę. Tai reiškia, kad turime pridėti filmą prie superherojaus. Kai abi santykių pusės yra tinkamai sukonfigūruotos taip, kad filme yra nuoroda į superherojų, o superherojus - į filmą, sujungimo lentelė taip pat bus tinkamai užpildyta.
Mes apibrėžėme savo du subjektus. Dabar pažvelkime į saugyklas, kurias naudosime, kad jas išsaugotume duomenų bazėje ir iš jos.
Patarimas! Padėkite abi stalo puses
Dažna klaida nustatyti tik vieną santykio pusę, išlaikyti esybę ir stebėti, ar prisijungimo lentelė tuščia. Nustačius abi santykių puses, tai bus išspręsta.
JPA saugyklos
Visą savo patvarumo kodą galėtume įdiegti tiesiogiai pavyzdinėje programoje, tačiau kurdami saugyklos klases galime atskirti patvarumo kodą nuo programos kodo. Kaip ir mes padarėme naudodamiesi „Knygų ir autorių“ programa 1 dalyje, sukursime „EntityManager“
ir tada naudokite jį, kad inicijuotumėte dvi saugyklas, po vieną kiekvienam objektui, kurį išlaikome.
3 sąraše rodomas šaltinio kodas „MovieRepository“
klasė.
Sąrašas 3. MovieRepository.java
paketas com.geekcap.javaworld.jpa.repository; importuoti com.geekcap.javaworld.jpa.model.Filmas; importuoti javax.persistence.EntityManager; importuoti java.util.List; importuoti java.util.Privaloma; public class MovieRepository {private EntityManager entityManager; public MovieRepository (EntityManager entityManager) {this.entityManager = subjektasVadas; } public Neprivalomas išsaugojimas (filmo filmas) {bandykite {entityManager.getTransaction (). begin (); entityManager.persist (filmas); entityManager.getTransaction (). įsipareigoti (); grįžti Neprivaloma.of (filmas); } sugavimas (e išimtis) {e.printStackTrace (); } return Neprivaloma.empty (); } public Pasirenkamas „findById“ (sveikasis skaičius) {Movie movie = entityManager.find (Movie.class, id); grąžinti filmą! = niekinis? Neprivaloma. Iš (filmo): Neprivaloma. } public List findAll () {return entityManager.createQuery ("from Movie"). getResultList (); } public void deleteById (sveikasis skaičius) {// Gauti filmą naudodami šį ID. Movie movie = entityManager.find (Movie.class, id); if (movie! = null) {try {// Pradėti operaciją, nes pakeisime duomenų bazės subjektąManager.getTransaction (). begin (); // Pašalinti visas superherojų nuorodas į šį filmą movie.getSuperHeroes (). ForEach (superHero -> {superHero.getMovies (). Pašalinti (filmas);}); // Dabar pašalinkite filmo subjektąManager.remove (movie); // Įvykdykite operacijos subjektąManager.getTransaction (). Įsipareigoti (); } gaudyti (e išimtis) {e.printStackTrace (); }}}}
„MovieRepository“
inicijuojamas „EntityManager“
, tada įrašo jį į kintamąjį narį, kad jį būtų galima naudoti patvarumo metoduose. Mes apsvarstysime kiekvieną iš šių metodų.
Atkaklumo metodai
Peržiūrėkime „MovieRepository“
patvarumo metodus ir pamatyti, kaip jie sąveikauja su „EntityManager“
atkaklumo metodai.