Programmering

Java-utholdenhet med JPA og Hibernate, del 2: Mange-til-mange-forhold

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
last ned Få koden Last ned kildekoden for eksempel applikasjoner som brukes i denne opplæringen. Skapt av Steven Haines for JavaWorld.

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 Haines

Noter 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 identifiserer SuperHero som en JPA-enhet.
  • @Bord kartlegger SuperHero enhet til "SUPER_HERO" -tabellen.

Legg også merke til Heltallid 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 Films.

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 SuperHeroprimæ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 tilsvarende Bli med på tabellen.
  • kaskade er konfigurert til CascadeType.PERSIST, som betyr at når en Film lagres tilsvarende SuperHero enheter bør også reddes.
  • hente forteller EntityManager at den skal hente en films superhelter ivrig: når den laster a Film, det skal også laste alle tilsvarende SuperHero 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 MovieRepositorys utholdenhetsmetoder og se hvordan de samhandler med EntityManagers utholdenhetsmetoder.

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