JPA-enhetsgraf

1. Oversikt

JPA 2.1 har introdusert Entity Graph-funksjonen som en mer sofistikert metode for å håndtere ytelsesbelastning.

Det gjør det mulig å definere en mal ved å gruppere de relaterte utholdenhetsfeltene som vi vil hente, og lar oss velge graftypen ved kjøretid.

I denne veiledningen forklarer vi mer detaljert hvordan du oppretter og bruker denne funksjonen.

2. Hva enhetsgrafen prøver å løse

Inntil JPA 2.0, brukte vi vanligvis for å laste en enhetsforening FetchType.LAT og FetchType.IVRIG som å hente strategier. Dette instruerer JPA-leverandøren om i tillegg å hente den relaterte tilknytningen eller ikke. Dessverre er denne metakonfigurasjonen statisk og tillater ikke å bytte mellom disse to strategiene ved kjøretid.

Hovedmålet med JPA Entity Graph er å forbedre kjøretidsytelsen når du laster enhetens relaterte tilknytninger og grunnleggende felt.

Kort sagt, JPA-leverandøren laster inn hele grafen i ett valgt spørsmål og unngår deretter å hente tilknytning til flere SELECT-spørsmål. Dette anses som en god tilnærming for å forbedre applikasjonsytelsen.

3. Definere modellen

Før vi begynner å utforske enhetsgrafen, må vi definere modellenhetene vi jobber med. La oss si at vi ønsker å lage et bloggside der brukere kan kommentere og dele innlegg.

Så først får vi en Bruker enhet:

@Entity public class User {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; privat strengnavn; privat streng e-post; // ...}

Brukeren kan dele forskjellige innlegg, så vi trenger også en Post enhet:

@Entity public class Innlegg {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat Lang id; privat strengemne; @OneToMany (mappedBy = "post") private Listekommentarer = ny ArrayList (); @ManyToOne (fetch = FetchType.LAZY) @JoinColumn privat brukerbruker; // ...}

Brukeren kan også kommentere de delte innleggene, så til slutt legger vi til en Kommentar enhet:

@Entity public class Kommentar {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat Lang id; privat streng svar; @ManyToOne (fetch = FetchType.LAZY) @JoinColumn privat innlegg; @ManyToOne (fetch = FetchType.LAZY) @JoinColumn privat brukerbruker; // ...}

Som vi kan se, er Post enheten har tilknytning til Kommentar og Bruker enheter. De Kommentar enheten har en tilknytning til Post og Bruker enheter.

Målet er da å laste inn følgende graf på forskjellige måter:

Innlegg -> bruker: Bruker -> kommentarer: Listekommentarer [0]: Kommentar -> bruker: Brukerkommentarer [1]: Kommentar -> bruker: Bruker

4. Laster relaterte enheter med FetchType Strategier

De FetchType metoden definerer to strategier for å hente data fra databasen:

  • FetchType.EAGER: Utholdenhetsleverandøren må laste det relaterte kommenterte feltet eller egenskapen. Dette er standard oppførsel for @Basic, @ManyToOne, og @OneToOne merkede felt.
  • FetchType.LAZY: Utholdenhetsleverandøren skal laste inn data når den først er tilgjengelig, men kan lastes ivrig. Dette er standard oppførsel for @OneToMany, @ManyToMany og @ ElementCollection-merkede felt.

For eksempel når vi laster a Post enhet, den relaterte Kommentar enheter er ikke lastet inn som standard FetchType siden @OneToMany er LAT. Vi kan overstyre denne oppførselen ved å endre FetchType til IVRIG:

@OneToMany (mappedBy = "post", fetch = FetchType.EAGER) private Listekommentarer = ny ArrayList ();

Til sammenligning når vi laster a Kommentar enhet, hans Post overordnet enhet er lastet inn som standardmodus for @ManyToOne, som er IVRIG. Vi kan også velge å ikke laste inn Post enhet ved å endre denne merknaden til LAT:

@ManyToOne (fetch = FetchType.LAZY) @JoinColumn (name = "post_id") privat innlegg;

Merk at som LAT er ikke et krav, kan utholdenhetsleverandøren fortsatt laste inn Post enhet ivrig hvis den vil. Så for å bruke denne strategien riktig, bør vi gå tilbake til den offisielle dokumentasjonen til den tilsvarende utholdenhetsleverandøren.

Nå, fordi vi har brukt merknader for å beskrive hentestrategien vår, vår definisjon er statisk, og det er ingen måte å bytte mellom LAT og IVRIG ved kjøretid.

Dette er hvor enhetsgrafen kommer til spill, som vi vil se i neste avsnitt.

5. Definere en enhetsgraf

For å definere en enhetsgraf kan vi enten bruke merknadene på enheten, eller så kan vi fortsette programmatisk ved hjelp av JPA API.

5.1. Definere en enhetsgraf med merknader

@NamedEntityGraph kommentar tillater å spesifisere attributtene som skal inkluderes når vi vil laste enheten og relaterte tilknytninger.

Så la oss først definere en enhetsgraf som laster inn Post og hans beslektede enheter Bruker og Kommentars:

@NamedEntityGraph (name = "post-entity-graph", attributeNodes = {@NamedAttributeNode ("subject"), @NamedAttributeNode ("user"), @NamedAttributeNode ("comments"),}) @Entity public class Post {@OneToMany (mappedBy = "post") private listekommentarer = ny ArrayList (); // ...}

I dette eksemplet har vi brukt @NamedAttributeNode for å definere relaterte enheter som skal lastes når rotenheten er lastet.

La oss nå definere en mer komplisert enhetsgraf der vi vil også laste inn Brukerer relatert til Kommentars.

For dette formålet bruker vi @NamedAttributeNode subgraph-attributt. Dette gjør det mulig å referere til et navngitt underbilde definert gjennom @NamedSubgraph kommentar:

@NamedEntityGraph (name = "post-entity-graph-with-comment-users", attributeNodes = {@NamedAttributeNode ("subject"), @NamedAttributeNode ("user"), @NamedAttributeNode (value = "comments", subgraph = " comments-subgraph "),}, subgraphs = {@NamedSubgraph (name =" comments-subgraph ", attributeNodes = {@NamedAttributeNode (" user ")})}) @Entity public class Post {@OneToMany (mappedBy =" post " ) private listekommentarer = ny ArrayList (); // ...}

Definisjonen av @NamedSubgraph merknader ligner på @NamedEntityGraph og tillater å spesifisere attributter for den relaterte tilknytningen. Ved å gjøre det kan vi konstruere en komplett graf.

I eksemplet ovenfor, med den definertepost-entity-graph-with-comment-users ' graf, kan vi laste Post, den relaterte Bruker, de Kommentarer og Brukerer relatert til Kommentarer.

Til slutt, merk at vi alternativt kan legge til definisjonen av enhetsgrafen ved hjelp av orm.xml distribusjonsbeskrivelse:

  ...     ... 

5.2. Definere en enhetsgraf med JPA API

Vi kan også definere enhetsgrafen gjennom EntityManager API ved å ringe createEntityGraph () metode:

EntityGraph entityGraph = entityManager.createEntityGraph (Post.class);

For å spesifisere attributtene til rotenheten bruker vi addAttributeNodes () metode.

entityGraph.addAttributeNodes ("subject"); entityGraph.addAttributeNodes ("bruker");

På samme måte, for å inkludere attributtene fra den relaterte enheten, bruker vi addSubgraph () for å konstruere en innebygd enhetsgraf og deretter vi addAttributeNodes () som vi gjorde ovenfor.

entityGraph.addSubgraph ("comments") .addAttributeNodes ("user");

Nå som vi har sett hvordan du lager enhetsgrafen, vil vi utforske hvordan du bruker den i neste avsnitt.

6. Bruke enhetsgrafen

6.1. Typer av enhetsgrafer

JPA definerer to egenskaper eller tips som vedvarende leverandør kan velge for å laste eller hente enhetsgrafen ved kjøretid:

  • javax.persistence.fetchgraph - Bare de angitte attributtene blir hentet fra databasen. Når vi bruker dvalemodus i denne opplæringen, kan vi merke oss at attributter statisk konfigurert som i motsetning til JPA-spesifikasjonene IVRIG er også lastet.
  • javax.persistence.loadgraph - I tillegg til de angitte attributtene, er attributter statisk konfigurert som IVRIG blir også hentet.

I begge tilfeller blir primærnøkkelen og versjonen, hvis noen, alltid lastet inn.

6.2. Laste inn en enhetsgraf

Vi kan hente enhetsgrafen på forskjellige måter.

La oss starte med å bruke EntityManager.find() metode. Som vi allerede har vist, er standardmodus basert på de statiske metastrategiene FetchType.EAGER og FetchType.LAZY.

Så la oss påkalle finne() metode og inspiser loggen:

Innlegg post = entityManager.find (Post.class, 1L);

Her er loggen fra Hibernate-implementering:

velg post0_.id som id1_1_0_, post0_.emne som subject2_1_0_, post0_.user_id som user_id3_1_0_ fra Post post0_ hvor post0_.id =?

Som vi kan se fra loggen, Bruker og Kommentar enheter er ikke lastet.

Vi kan overstyre denne standardoppførselen ved å påkalle den overbelastede finne() metode som godtar hint som en Kart. Vi kan da oppgi grafypen som vi vil laste inn:

EntityGraph entityGraph = entityManager.getEntityGraph ("post-entity-graph"); Kartegenskaper = nytt HashMap (); properties.put ("javax.persistence.fetchgraph", entityGraph); Innlegg post = entityManager.find (Post.class, id, egenskaper);

Hvis vi ser igjen i loggen, kan vi se at disse enhetene nå er lastet og bare i ett valgt spørsmål:

velg innlegg0_.id som id1_1_0_, innlegg0_.emne som emne2_1_0_, innlegg0_.bruker_id som bruker_id3_1_0_, kommentarer1_.post_id som innlegg_id3_0_1_, kommentarer1_.id som id1_0_1_, kommentarer1_.id som id1_0_2_, kommentarer1_.post_id_1. .bruker_id som bruker_id4_0_2_, bruker2_.id som id1_2_3_, bruker2_.email som e-post2_2_3_, bruker2_.navn som navn3_2_3_ fra Innlegg post0_ venstre ytre delta Kommentar kommentarer1_ på post0_.id = kommentarer1_.post_id venstre ytre bli med Bruker bruker2_ på post0_.user_id = bruker2. id der post0_.id =?

La oss se hvordan vi kan oppnå det samme ved hjelp av JPQL:

EntityGraph entityGraph = entityManager.getEntityGraph ("post-entity-graph-with-comment-users"); Post post = entityManager.createQuery ("velg p fra Post p der p.id =: id", Post.class) .setParameter ("id", id) .setHint ("javax.persistence.fetchgraph", entityGraph) .getSingleResult ();

Og til slutt, la oss ta en titt på a Kriterier API-eksempel:

EntityGraph entityGraph = entityManager.getEntityGraph ("post-entity-graph-with-comment-users"); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder (); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery (Post.class); Rotrot = criteriaQuery.from (Post.class); criteriaQuery.where (criteriaBuilder.equal (root.get ("id"), id)); TypedQuery typedQuery = entityManager.createQuery (criteriaQuery); typedQuery.setHint ("javax.persistence.loadgraph", entityGraph); Innleggsinnlegg = typedQuery.getSingleResult ();

I hver av disse, grafstypen er gitt som et hint. Mens vi i det første eksemplet brukte Kart, i de to senere eksemplene har vi brukt setHint () metode.

7. Konklusjon

I denne artikkelen har vi utforsket bruk av JPA-enhetsgrafen til å hente en dynamisk Enhet og dets assosiasjoner.

Beslutningen tas i løpetid der vi velger å laste inn eller ikke den tilknyttede tilknytningen.

Ytelse er åpenbart en nøkkelfaktor å ta hensyn til når du designer JPA-enheter. JPA-dokumentasjonen anbefaler at du bruker FetchType.LAZY strategi når det er mulig, og enhetsgrafen når vi trenger å laste inn en tilknytning.

Som vanlig er all koden tilgjengelig på GitHub.


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