Første halvdel av denne veiledningen introduserte grunnleggende om Java Persistence API og viste deg hvordan du konfigurerer et JPA-program ved hjelp av Hibernate 5.3.6 og Java 8. Hvis du har lest denne veiledningen og studert eksempelapplikasjonen, vet du det grunnleggende modellering av JPA-enheter og mange-til-en-forhold i JPA. Du har også hatt litt øvelse i å skrive navngitte spørsmål med JPA Query Language (JPQL).
I denne andre halvdelen av opplæringen går vi dypere med JPA og Hibernate. Du lærer hvordan du kan modellere et mange-til-mange forhold mellom Film
og SuperHero
enheter, sett opp individuelle arkiver for disse enhetene, og vedvar enhetene til H2-minnedatabasen. Du vil også lære mer om rollen som kaskadeoperasjoner i JPA, og få tips om hvordan du velger en CascadeType
strategi for enheter i databasen. Til slutt trekker vi sammen et fungerende program som du kan kjøre i IDE eller på kommandolinjen.
Denne opplæringen fokuserer på JPA-grunnleggende, men sørg for å sjekke ut disse Java-tipsene som introduserer mer avanserte emner i JPA:
- Arveforhold i JPA og Hibernate
- Komposittaster i JPA og Hibernate
Mange til mange forhold i JPA
Mange-til-mange-forhold definere enheter som begge sider av forholdet kan ha flere referanser til hverandre for. For vårt eksempel skal vi modellere filmer og superhelter. I motsetning til forfatterne og bøkene fra del 1, kan en film ha flere superhelter, og en superhelt kan vises i flere filmer. Våre superhelter, Ironman og Thor, vises begge i to filmer, "The Avengers" og "Avengers: Infinity War."
For å modellere dette mange-til-mange-forholdet ved hjelp av JPA, trenger vi tre tabeller:
- FILM
- SUPER_HERO
- SUPERHERO_MOVIES
Figur 1 viser domenemodellen med de tre tabellene.
Steven HainesNoter det SuperHero_Movies
er en bli med på bordet mellom Film
og SuperHero
bord. I JPA er et sammenføyningstabell en spesiell type bord som letter forholdet mellom mange og mange.
Enveis eller toveis?
I JPA bruker vi @ManyToMany
kommentar for å modellere mange-til-mange-forhold. Denne typen forhold kan være ensrettet eller toveis:
- I en ensrettet forhold bare en enhet i forholdet peker den andre.
- I en toveis forhold begge enhetene peker mot hverandre.
Eksemplet vårt er toveis, noe som betyr at en film peker på alle superheltene, og en superhelt peker på alle filmene deres. I et toveis, mange-til-mange forhold, en enhet eier forholdet og det andre er kartlagt til forholdet. Vi bruker kartlagt av
attributt til @ManyToMany
kommentar for å lage denne kartleggingen.
Oppføring 1 viser kildekoden for SuperHero
klasse.
Oppføring 1. SuperHero.java
pakke com.geekcap.javaworld.jpa.model; importere javax.persistence.CascadeType; importere javax.persistence.Entity; importere javax.persistence.FetchType; importere javax.persistence.GeneratedValue; importere javax.persistence.Id; importere javax.persistence.JoinColumn; importere javax.persistence.JoinTable; importere javax.persistence.ManyToMany; importere javax.persistence.Table; importere java.util.HashSet; importere java.util.Set; importere java.util.stream.Collectors; @Entity @Table (name = "SUPER_HERO") offentlig klasse SuperHero {@Id @GeneratedValue private Integer id; privat strengnavn; @ManyToMany (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable (name = "SuperHero_Movies", joinColumns = {@JoinColumn (name = "superhero_id")}, inverseJoinColumn = {@JoinColumn (name = "movie_id) }) private Set-filmer = nye HashSet (); public SuperHero () {} public SuperHero (Integer id, String name) {this.id = id; this.name = navn; } offentlig SuperHero (strengnavn) {this.name = navn; } public Integer getId () {return id; } public void setId (Integer id) {this.id = id; } public String getName () {return name; } public void setName (String name) {this.name = name; } public Set getMovies () {returner filmer; } @ Override public String toString () {return "SuperHero {" + "id =" + id + ", + name +" \ '' + ", + movies.stream (). Map (Film :: getTitle) .collect (Collectors.toList ()) + "\ '' + '}'; }}
De SuperHero
klasse har et par kommentarer som skal være kjent fra del 1:
@Enhet
identifisererSuperHero
som en JPA-enhet.@Bord
kartleggerSuperHero
enhet til "SUPER_HERO" -tabellen.
Legg også merke til Heltall
id
felt, som spesifiserer at tabellens primære nøkkel genereres automatisk.
Neste skal vi se på @ManyToMany
og @JoinTable
kommentarer.
Henter strategier
Tingen å legge merke til i @ManyToMany
kommentar er hvordan vi konfigurerer hentestrategi, som kan være lat eller ivrig. I dette tilfellet har vi satt hente
til IVRIG
, slik at når vi henter en SuperHero
fra databasen, vil vi også automatisk hente alt det tilsvarende Film
s.
Hvis vi valgte å utføre en LAT
hent i stedet, ville vi bare hente hver Film
som det ble spesifikt åpnet. Lat henting er bare mulig mens SuperHero
er festet til EntityManager
; ellers får tilgang til en superheltfilm et unntak. Vi ønsker å kunne få tilgang til en superheltfilm på forespørsel, så i dette tilfellet velger vi IVRIG
hentestrategi.
CascadeType.PERSIST
Kaskadeoperasjoner definere hvordan superhelter og deres tilhørende filmer fortsetter til og fra databasen. Det er en rekke kaskadetypekonfigurasjoner å velge mellom, og vi snakker mer om dem senere i denne opplæringen. For øyeblikket er det bare å merke seg at vi har satt inn kaskade
tilskrive CascadeType.PERSIST
, som betyr at når vi lagrer en superhelt, blir filmene også lagret.
Bli med på bord
Bli med på tabellen
er en klasse som legger til rette for mange-til-mange-forholdet mellom SuperHero
og Film
. I denne klassen definerer vi tabellen som vil lagre primærnøklene for begge SuperHero
og Film
enheter.
Oppføring 1 spesifiserer at navnet på tabellen vil være SuperHero_Movies
. De bli med i kolonnen vil være superhelt_id
, og invers sammenføyningskolonne vil være movie_id
. De SuperHero
enheten eier forholdet, slik at kolonnen for tilknytning fylles med SuperHero
primærnøkkel. Den inverse sammenføyningskolonnen refererer deretter til enheten på den andre siden av forholdet, altså Film
.
Basert på disse definisjonene i oppføring 1, forventer vi at det blir opprettet en ny tabell med navnet SuperHero_Movies
. Tabellen vil ha to kolonner: superhelt_id
, som refererer til id
kolonnen i SUPERHERO
bord og movie_id
, som refererer til id
kolonnen i FILM
bord.
Filmklassen
Oppføring 2 viser kildekoden for Film
klasse. Husk at i en toveis forhold, eier en enhet forholdet (i dette tilfellet SuperHero
) mens den andre er kartlagt til forholdet. Koden i oppføring 2 inkluderer forholdskartleggingen som brukes på Film
klasse.
Oppføring 2. Movie.java
pakke com.geekcap.javaworld.jpa.model; importere javax.persistence.CascadeType; importere javax.persistence.Entity; importere javax.persistence.FetchType; importere javax.persistence.GeneratedValue; importere javax.persistence.Id; importere javax.persistence.ManyToMany; importere javax.persistence.Table; importere java.util.HashSet; importere java.util.Set; @Entity @Table (name = "MOVIE") offentlig klasse Film {@Id @GeneratedValue privat Heltall-id; privat strengetittel; @ManyToMany (mappedBy = "films", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) private Sett superHeroes = ny HashSet (); public Movie () {} public Movie (Integer id, String title) {this.id = id; this.title = tittel; } offentlig film (strengetittel) {this.title = title; } public Integer getId () {return id; } public void setId (Integer id) {this.id = id; } public String getTitle () {return title; } public void setTitle (String title) {this.title = title; } public Set getSuperHeroes () {return superHeroes; } offentlig tomrom addSuperHero (SuperHero superHero) {superHeroes.add (superHero); superHero.getMovies (). legg til (dette); } @ Override public String toString () {return "Movie {" + "id =" + id + ", + title +" \ '' + '}'; }}
Følgende egenskaper brukes på @ManyToMany
kommentar i oppføring 2:
kartlagt av
refererer feltnavnet påSuperHero
klasse som styrer det mange-til-mange-forholdet. I dette tilfellet refererer den til filmer felt, som vi definerte i Listing 1 med det tilsvarendeBli med på tabellen
.kaskade
er konfigurert tilCascadeType.PERSIST
, som betyr at når enFilm
lagres tilsvarendeSuperHero
enheter bør også reddes.hente
fortellerEntityManager
at den skal hente en films superhelter ivrig: når den laster aFilm
, det skal også laste alle tilsvarendeSuperHero
enheter.
Noe annet å merke seg om Film
klassen er dens addSuperHero ()
metode.
Når du konfigurerer enheter for utholdenhet, er det ikke nok å bare legge til en superhelt i en film. vi må også oppdatere den andre siden av forholdet. Dette betyr at vi må legge filmen til superhelten. Når begge sider av forholdet er konfigurert riktig, slik at filmen har en referanse til superhelten og superhelten har en referanse til filmen, vil også sammenføyningstabellen være riktig fylt ut.
Vi har definert våre to enheter. La oss nå se på arkivene vi vil bruke til å vedvare dem til og fra databasen.
Tips! Sett begge sider av bordet
Det er en vanlig feil å bare sette den ene siden av forholdet, vedvare enheten, og deretter observere at sammenføyningstabellen er tom. Å sette begge sider av forholdet vil løse dette.
JPA-arkiver
Vi kunne implementere all utholdenhetskoden vår direkte i eksempelprogrammet, men ved å opprette depotklasser kan vi skille utholdenhetskode fra applikasjonskode. Akkurat som vi gjorde med Bøker og forfattere-applikasjonen i del 1, oppretter vi en EntityManager
og bruk den deretter til å initialisere to arkiver, ett for hver enhet vi vedvarer.
Oppføring 3 viser kildekoden for MovieRepository
klasse.
Oppføring 3. MovieRepository.java
pakke com.geekcap.javaworld.jpa.repository; import com.geekcap.javaworld.jpa.model.Movie; importere javax.persistence.EntityManager; importere java.util.List; importere java.util.Optional; offentlig klasse MovieRepository {private EntityManager entityManager; offentlig MovieRepository (EntityManager entityManager) {this.entityManager = entityManager; } offentlig Valgfri lagring (filmfilm) {prøv {entityManager.getTransaction (). start (); entityManager.persist (film); entityManager.getTransaction (). commit (); retur Valgfritt. av (film); } fange (Unntak e) {e.printStackTrace (); } returner Optional.empty (); } offentlig Valgfri findById (heltall-id) {Movie movie = entityManager.find (Movie.class, id); returner film! = null? Optional.of (film): Optional.empty (); } offentlig liste findAll () {return entityManager.createQuery ("fra film"). getResultList (); } public void deleteById (Integer id) {// Hent filmen med denne IDen Movie movie = entityManager.find (Movie.class, id); hvis (film! = null) {prøv {// Start en transaksjon fordi vi skal endre database entityManager.getTransaction (). begin (); // Fjern alle referanser til denne filmen av superhelter movie.getSuperHeroes (). ForEach (superHero -> {superHero.getMovies (). Remove (film);}); // Fjern nå filmen entityManager.remove (film); // Forplikt transaksjonen entityManager.getTransaction (). Commit (); } fange (Unntak e) {e.printStackTrace (); }}}}
De MovieRepository
initialiseres med en EntityManager
, lagrer den deretter til en medlemsvariabel som skal brukes i dens utholdenhetsmetoder. Vi vil vurdere hver av disse metodene.
Persistensmetoder
La oss se gjennom MovieRepository
s utholdenhetsmetoder og se hvordan de samhandler med EntityManager
s utholdenhetsmetoder.