Programavimas

„Java“ atkaklumas naudojant JPA ir „Hibernate“, 2 dalis. Ryšiai tarp daugelio

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
atsisiųsti Gauti kodą Atsisiųskite šioje pamokoje naudojamų programų, pavyzdžiui, šaltinio kodą. Stevenas Hainesas sukūrė „JavaWorld“.

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.

Stevenas Hainesas

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 identifikuoja Super herojus kaip JPA subjektas.
  • @ Lentelė žemėlapiai Super herojus „SUPER_HERO“ lentelėje.

Taip pat atkreipkite dėmesį į Sveikasis skaičiusid 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 Filmass.

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 herojuspagrindinis 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 a Filmas yra išsaugotas atitinkamas Super 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 a Filmas, jis taip pat turėtų įkelti visus atitinkamus Super 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.