JPA Entiteitsgrafiek

1. Overzicht

JPA 2.1 heeft de Entity Graph-functie geïntroduceerd als een meer geavanceerde methode om met het laden van prestaties om te gaan.

Hiermee kan een sjabloon worden gedefinieerd door de gerelateerde persistentievelden die we willen ophalen te groeperen en kunnen we het grafiektype tijdens runtime kiezen.

In deze zelfstudie leggen we meer in detail uit hoe u deze functie kunt maken en gebruiken.

2. Wat de entiteitsgrafiek probeert op te lossen

Tot JPA 2.0 gebruikten we meestal FetchType.LUI en FetchType.EAGER als ophaalstrategieën. Dit geeft de JPA-provider de opdracht om de gerelateerde associatie aanvullend op te halen of niet. Helaas is deze meta-configuratie statisch en staat niet toe om tijdens runtime tussen deze twee strategieën te schakelen.

Het belangrijkste doel van de JPA Entity Graph is vervolgens om de runtime-prestaties te verbeteren bij het laden van de gerelateerde associaties en basisvelden van de entiteit.

Kort gezegd laadt de JPA-provider alle grafieken in één geselecteerde query en vermijdt vervolgens de associatie met het ophalen van meer SELECT-query's. Dit wordt beschouwd als een goede benadering om de prestaties van applicaties te verbeteren.

3. Het model definiëren

Voordat we de Entiteitsgrafiek gaan verkennen, moeten we de modelentiteiten definiëren waarmee we werken. Stel dat we een blogsite willen maken waarop gebruikers kunnen reageren en berichten kunnen delen.

Dus eerst hebben we een Gebruiker entiteit:

@Entity openbare klasse Gebruiker {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) privé Lange id; private String naam; privé String-e-mail; // ...}

De gebruiker kan verschillende berichten delen, dus we hebben ook een Post entiteit:

@Entity openbare klasse Post {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) privé Lange id; privé String-onderwerp; @OneToMany (mappedBy = "post") privélijst commentaren = nieuwe ArrayList (); @ManyToOne (fetch = FetchType.LAZY) @JoinColumn privégebruiker; // ...}

De gebruiker kan ook reageren op de gedeelde berichten, dus tot slot voegen we een Commentaar entiteit:

@Entity openbare klasse Opmerking {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) privé Lange id; private String antwoord; @ManyToOne (fetch = FetchType.LAZY) @JoinColumn privébericht; @ManyToOne (fetch = FetchType.LAZY) @JoinColumn privégebruiker; // ...}

Zoals we kunnen zien, is de Post entiteit heeft een associatie met de Commentaar en Gebruiker entiteiten. De Commentaar entiteit heeft een koppeling met de Post en Gebruiker entiteiten.

Het doel is dan om de volgende grafiek op verschillende manieren te laden:

Post -> gebruiker: Gebruiker -> commentaren: Lijst commentaren [0]: Commentaar -> gebruiker: Gebruikers commentaren [1]: Commentaar -> gebruiker: Gebruiker

4. Gerelateerde entiteiten laden met FetchType Strategieën

De FetchType methode definieert twee strategieën voor het ophalen van gegevens uit de database:

  • FetchType.EAGER: De persistentieleverancier moet het gerelateerde geannoteerde veld of de bijbehorende eigenschap laden. Dit is het standaardgedrag voor @Basic, @ManyToOne, en @Een op een geannoteerde velden.
  • FetchType.LAZY: De persistentieprovider moet gegevens laden wanneer deze voor het eerst worden geopend, maar kan gretig worden geladen. Dit is het standaardgedrag voor @OneToMany, @ManyToMany en @ ElementCollection-geannoteerde velden.

Als we bijvoorbeeld een Post entiteit, de gerelateerde Commentaar entiteiten worden niet standaard geladen FetchType sinds @Een te veel is LUI. We kunnen dit gedrag opheffen door de FetchType naar EAGER:

@OneToMany (mappedBy = "post", fetch = FetchType.EAGER) private List comments = nieuwe ArrayList ();

Ter vergelijking: wanneer we een Commentaar entiteit, zijn Post bovenliggende entiteit wordt geladen als de standaardmodus voor @ManyToOne, dat is EAGER. We kunnen er ook voor kiezen om de Post entiteit door deze annotatie te wijzigen in LUI:

@ManyToOne (fetch = FetchType.LAZY) @JoinColumn (name = "post_id") privé bericht;

Merk op dat als LUI is geen vereiste, de persistentieprovider kan het Post entiteit gretig als het wil. Dus om deze strategie correct te gebruiken, moeten we teruggaan naar de officiële documentatie van de corresponderende persistentieprovider.

Omdat we annotaties hebben gebruikt om onze ophaalstrategie te beschrijven, onze definitie is statisch en er is geen manier om te schakelen tussen de LUI en EAGER tijdens runtime.

Dit is waar de Entiteitsgrafiek in het spel komt, zoals we in de volgende sectie zullen zien.

5. Een entiteitsgrafiek definiëren

Om een ​​entiteitsgrafiek te definiëren, kunnen we de annotaties op de entiteit gebruiken of we kunnen programmatisch verder gaan met behulp van de JPA API.

5.1. Een entiteitsgrafiek met annotaties definiëren

De @NamedEntityGraph annotatie maakt het mogelijk om de attributen op te geven die moeten worden opgenomen wanneer we de entiteit en de gerelateerde associaties willen laden.

Laten we dus eerst een entiteitsgrafiek definiëren die de Post en zijn verwante entiteiten Gebruiker en Commentaars:

@NamedEntityGraph (name = "post-entity-graph", attributeNodes = {@NamedAttributeNode ("subject"), @NamedAttributeNode ("user"), @NamedAttributeNode ("comments"),}) @Entity public class Post {@OneToMany (mappedBy = "post") privélijst commentaren = nieuwe ArrayList (); // ...}

In dit voorbeeld hebben we de @NamedAttributeNode om de gerelateerde entiteiten te definiëren die moeten worden geladen wanneer de hoofdentiteit wordt geladen.

Laten we nu een meer gecompliceerde Entiteitsgrafiek definiëren waar we ook de Gebruikers gerelateerd aan de Commentaars.

Voor dit doel gebruiken we de @NamedAttributeNode subgraaf attribuut. Hierdoor kan worden verwezen naar een benoemde subgraaf die is gedefinieerd via de @NamedSubgraph annotatie:

@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 List comments = nieuwe ArrayList (); // ...}

De definitie van de @NamedSubgraph annotatie is vergelijkbaar met de @NamedEntityGraph en maakt het mogelijk om attributen van de gerelateerde associatie te specificeren. Op deze manier kunnen we een volledige grafiek maken.

In het bovenstaande voorbeeld, met de gedefinieerde ‘post-entiteit-grafiek-met-commentaar-gebruikers ' grafiek, kunnen we de Post, de gerelateerde Gebruiker, de Opmerkingen en de Gebruikers gerelateerd aan de Opmerkingen.

Merk ten slotte op dat we als alternatief de definitie van de entiteitsgrafiek kunnen toevoegen met de orm.xml implementatie descriptor:

  ...     ... 

5.2. Een entiteitsgrafiek definiëren met de JPA-API

We kunnen de Entiteitsgrafiek ook definiëren via de EntityManager API door het createEntityGraph () methode:

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

Om de attributen van de hoofdentiteit te specificeren, gebruiken we de addAttributeNodes () methode.

entiteitGraph.addAttributeNodes ("onderwerp"); entiteitGraph.addAttributeNodes ("gebruiker");

Om de attributen van de gerelateerde entiteit op te nemen, gebruiken we op dezelfde manier de addSubgraph () om een ​​ingesloten entiteitsgrafiek te construeren en vervolgens de addAttributeNodes () zoals we hierboven hebben gedaan.

entiteitGraph.addSubgraph ("reacties") .addAttributeNodes ("gebruiker");

Nu we hebben gezien hoe we de entiteitsgrafiek kunnen maken, zullen we in de volgende sectie onderzoeken hoe we deze kunnen gebruiken.

6. De entiteitsgrafiek gebruiken

6.1. Typen entiteitsgrafieken

JPA definieert twee eigenschappen of hints waarmee de persistentieleverancier kan kiezen om de entiteitsgrafiek tijdens runtime te laden of op te halen:

  • javax.persistence.fetchgraph - Alleen de opgegeven attributen worden opgehaald uit de database. Aangezien we Hibernate in deze tutorial gebruiken, kunnen we opmerken dat, in tegenstelling tot de JPA-specificaties, attributen statisch geconfigureerd zijn als EAGER worden ook geladen.
  • javax.persistence.loadgraph - Naast de gespecificeerde attributen, attributen die statisch zijn geconfigureerd als EAGER worden ook opgehaald.

In beide gevallen worden de primaire sleutel en de eventuele versie altijd geladen.

6.2. Een entiteitsgrafiek laden

We kunnen de Entiteitsgrafiek op verschillende manieren ophalen.

Laten we beginnen met het gebruik van de EntityManager.find() methode. Zoals we al hebben laten zien, is de standaardmodus gebaseerd op de statische metastrategieën FetchType.EAGER en FetchType.LAZY.

Dus laten we de vind() methode en inspecteer het logboek:

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

Hier is het logboek van de Hibernate-implementatie:

selecteer post0_.id als id1_1_0_, post0_.subject als subject2_1_0_, post0_.user_id als user_id3_1_0_ uit Post post0_ waar post0_.id =?

Zoals we kunnen zien in het logboek, is de Gebruiker en Commentaar entiteiten worden niet geladen.

We kunnen dit standaardgedrag opheffen door het overloaded vind() methode die hints accepteert als een Kaart. We kunnen dan geef het grafiektype op dat we willen laden:

EntityGraph entityGraph = entityManager.getEntityGraph ("post-entity-graph"); Kaarteigenschappen = nieuwe HashMap (); properties.put ("javax.persistence.fetchgraph", entityGraph); Post post = entityManager.find (Post.class, id, eigenschappen);

Als we opnieuw in het logboek kijken, kunnen we zien dat deze entiteiten nu zijn geladen en slechts in één geselecteerde query:

selecteer post0_.id als id1_1_0_, post0_.subject als subject2_1_0_, post0_.user_id als user_id3_1_0_, comments1_.post_id als post_id3_0_1_, comments1_.id als id1_0_1_, comments1_.id als id1_0_2_, comments_1_.id_id_id0 .user_id als user_id4_0_2_, user2_.id als id1_2_3_, user2_.email als email2_2_3_, user2_.name als naam3_2_3_ van Post post0_ left outer join Comment comments1_ op post0_.id = comments1_.post_id left outer join Gebruiker user2_ op post0_.user_id = gebruiker2_id = gebruiker. id waar post0_.id =?

Laten we eens kijken hoe we hetzelfde kunnen bereiken met JPQL:

EntityGraph entityGraph = entityManager.getEntityGraph ("post-entiteit-grafiek-met-commentaar-gebruikers"); Post post = entityManager.createQuery ("select p from Post p where p.id =: id", Post.class) .setParameter ("id", id) .setHint ("javax.persistence.fetchgraph", entityGraph) .getSingleResult ();

En tot slot, laten we eens kijken naar een Criteria API-voorbeeld:

EntityGraph entityGraph = entityManager.getEntityGraph ("post-entiteit-grafiek-met-commentaar-gebruikers"); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder (); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery (Post.class); Root root = criteriaQuery.from (Post.class); criteriaQuery.where (criteriaBuilder.equal (root.get ("id"), id)); TypedQuery typedQuery = entityManager.createQuery (criteriaQuery); typedQuery.setHint ("javax.persistence.loadgraph", entityGraph); Bericht plaatsen = typedQuery.getSingleResult ();

In elk van deze, het grafiektype wordt als hint gegeven. Terwijl we in het eerste voorbeeld de Kaart, in de twee latere voorbeelden hebben we de setHint () methode.

7. Conclusie

In dit artikel hebben we het gebruik van de JPA-entiteitsgrafiek onderzocht om dynamisch een Entiteit en zijn verenigingen.

De beslissing wordt genomen tijdens runtime waarin we ervoor kiezen om de gerelateerde associatie al dan niet te laden.

Prestaties zijn uiteraard een sleutelfactor waarmee rekening moet worden gehouden bij het ontwerpen van PPV-entiteiten. De JPA-documentatie raadt het gebruik van de FetchType.LAZY strategie waar mogelijk, en de Entiteitsgrafiek wanneer we een associatie moeten laden.

Zoals gewoonlijk is alle code beschikbaar op GitHub.