Beknopte handleiding voor EntityManager # getReference ()

1. Inleiding

De getReference () methode van de EntityManager class maakt sinds de eerste versie deel uit van de JPA-specificatie. Deze methode brengt echter sommige ontwikkelaars in verwarring omdat het gedrag ervan varieert afhankelijk van de onderliggende persistentieleverancier.

In deze tutorial gaan we uitleggen hoe je het getReference () methode in Slaapstand EntityManager.

2. EntityManager Ophaalbewerkingen

Allereerst zullen we bekijken hoe we entiteiten kunnen ophalen met hun primaire sleutels. Zonder vragen te stellen, EntityManager biedt ons twee basismethoden om dit te bereiken.

2.1. vind()

vind() is de meest gebruikelijke methode om entiteiten op te halen:

Game game = entityManager.find (Game.class, 1L); 

Deze methode initialiseert de entiteit wanneer we daarom vragen.

2.2. getReference ()

Net als bij de vind() methode, getReference () is ook een andere manier om entiteiten op te halen:

Game game = entityManager.getReference (Game.class, 1L); 

Het geretourneerde object is echter een entiteitsproxy waarvan alleen het primaire sleutelveld is geïnitialiseerd. De andere velden blijven uitgeschakeld, tenzij we er lui om vragen.

Laten we vervolgens kijken hoe deze twee methoden zich in verschillende scenario's gedragen.

3. Een voorbeeld van een use-case

Om te demonstreren EntityManager ophaalbewerkingen, we maken twee modellen, Spel en Speler, als ons domein dat veel spelers bij hetzelfde spel betrokken kunnen zijn.

3.1. Domeinmodel

Laten we eerst een entiteit definiëren met de naam Spel:

@Entity openbare klasse Game {@Id privé Lange id; private String naam; // standaard constructeurs, getters, setters} 

Vervolgens definiëren we onze Speler entiteit:

@Entity openbare klasse Speler {@Id privé Lange id; private String naam; // standaard constructeurs, getters, setters} 

3.2. Relaties configureren

We moeten configureer een @ManyToOne relatie van Speler naar Spel. Dus laten we een spel eigendom aan onze Speler entiteit:

@ManyToOne privégame-spel; 

4. Testgevallen

Voordat we beginnen met het schrijven van onze testmethoden, is het een goede gewoonte om onze testgegevens afzonderlijk te definiëren:

entiteitManager.getTransaction (). begin (); entiteitManager.persist (nieuwe game (1L, "Game 1")); entiteitManager.persist (nieuwe game (2L, "Game 2")); entiteitManager.persist (nieuwe speler (1L, "Speler 1")); entityManager.persist (nieuwe speler (2L, "Player 2")); entityManager.persist (nieuwe speler (3L, "Player 3")); entiteitManager.getTransaction (). commit (); 

Bovendien moeten we, om onderliggende SQL-query's te onderzoeken, configureer Hibernate's slaapstand. show_sql eigendom in onze persistence.xml:

4.1. Entiteitsvelden bijwerken

Eerst kijken we wat de meest gebruikelijke manier is om een ​​entiteit bij te werken met behulp van de vind() methode.

Laten we dus een testmethode schrijven om het Spel entiteit eerst, werk dan eenvoudig zijn naam veld:

Game game1 = entityManager.find (Game.class, 1L); game1.setName ("Game bijgewerkt 1"); entiteitManager.persist (game1); 

Het uitvoeren van de testmethode toont ons de uitgevoerde SQL-queries:

Slaapstand: selecteer game0_.id als id1_0_0_, game0_.name als naam2_0_0_ van Game game0_ waar game0_.id =? Slaapstand: update Game set name =? waar id =? 

Zoals we opmerken, de SELECTEER query lijkt in zo'n geval niet nodig. Omdat we geen enkel veld van de Spel entiteit vóór onze updatebewerking, vragen we ons af of er een manier is om alleen de BIJWERKEN vraag.

Laten we dus eens kijken hoe de getReference () methode gedraagt ​​zich in hetzelfde scenario:

Game game1 = entityManager.getReference (Game.class, 1L); game1.setName ("Game Bijgewerkt 2"); entiteitManager.persist (game1); 

Verrassend genoeg, het resultaat van de lopende testmethode is nog steeds hetzelfde en we zien de SELECTEER vraag blijft.

Zoals we kunnen zien, voert Hibernate een SELECTEER vraag wanneer we gebruiken getReference () om een ​​entiteitsveld bij te werken.

Daarom de ... gebruiken getReference () methode vermijdt de extra niet SELECTEER vraag of we een setter van de velden van de entiteitproxy uitvoeren.

4.2. Entiteiten verwijderen

Een soortgelijk scenario kan zich voordoen wanneer we verwijderbewerkingen uitvoeren.

Laten we nog twee testmethoden definiëren om een Speler entiteit:

Player player2 = entityManager.find (Player.class, 2L); entiteitManager.remove (player2); 
Player player3 = entityManager.getReference (Player.class, 3L); entiteitManager.remove (player3); 

Het uitvoeren van deze testmethoden laat ons dezelfde vragen zien:

Slaapstand: selecteer player0_.id als id1_1_0_, player0_.game_id als game_id3_1_0_, player0_.name als name2_1_0_, game1_.id als id1_0_1_, game1_.name als naam2_0_1_ van Player player0_ linksbuiten doe mee met Game game1_ op player0_.game_id = player0_.game_id = .id =? Slaapstand: verwijderen uit speler waar id =? 

Evenzo is het resultaat voor verwijderingsbewerkingen vergelijkbaar. Zelfs als we geen velden van de Speler entiteit, voert Hibernate een extra SELECTEER vraag ook.

Vandaar, er is geen verschil of we kiezen getReference () of vind() methode wanneer we een bestaande entiteit verwijderen.

Op dit punt vragen we ons af, doet getReference () enig verschil maken dan? Laten we verder gaan met entiteitsrelaties en erachter komen.

4.3. Entiteitsrelaties bijwerken

Een andere veel voorkomende use-case doet zich voor wanneer we relaties tussen onze entiteiten moeten bewaren.

Laten we nog een methode toevoegen om een SpelerDeelname aan een Spel door simpelweg het Speler‘S spel eigendom:

Game game1 = entityManager.find (Game.class, 1L); Player player1 = entityManager.find (Player.class, 1L); player1.setGame (game1); entiteitManager.persist (speler1); 

Het uitvoeren van de test geeft ons nog een keer een vergelijkbaar resultaat en we kunnen nog steeds de SELECTEER queries bij gebruik van de vind() methode:

Slaapstand: selecteer game0_.id als id1_0_0_, game0_.name als naam2_0_0_ van Game game0_ waar game0_.id =? Slaapstand: selecteer player0_.id als id1_1_0_, player0_.game_id als game_id3_1_0_, player0_.name als name2_1_0_, game1_.id als id1_0_1_, game1_.name als naam2_0_1_ van Player player0_ linksbuiten sluit je aan bij Game game1_ op player0_.game_id = player0_.game_id = .id =? Slaapstand: update spelerset game_id = ?, name =? waar id =? 

Laten we nu nog een test definiëren zie hoe de getReference () methode werkt in dit geval:

Game game2 = entityManager.getReference (Game.class, 2L); Player player1 = entityManager.find (Player.class, 1L); player1.setGame (game2); entiteitManager.persist (speler1); 

Hopelijk geeft het uitvoeren van de test ons het verwachte gedrag:

Slaapstand: selecteer player0_.id als id1_1_0_, player0_.game_id als game_id3_1_0_, player0_.name als name2_1_0_, game1_.id als id1_0_1_, game1_.name als naam2_0_1_ van Player player0_ linksbuiten sluit je aan bij Game game1_ op player0_.game_id = player0_.game_id = .id =? Slaapstand: update spelerset game_id = ?, name =? waar id =? 

En we zien Hibernate voert geen SELECTEER query voor de Spel entiteit wanneer we gebruiken getReference () deze keer.

Het lijkt dus een goede gewoonte om te kiezen getReference () in dit geval. Dat komt door een proxy Spel entiteit is voldoende om de relatie te creëren vanuit de Speler entiteit - de Spel entiteit hoeft niet te worden geïnitialiseerd.

Bijgevolg, gebruik makend van getReference () kan onnodige rondreizen naar onze database elimineren wanneer we entiteitsrelaties bijwerken.

5. Slaapstand op het eerste niveau van de cache

Dat kan soms verwarrend zijn beide methoden vind() en getReference () mag er geen uitvoeren SELECTEER vragen in sommige gevallen.

Laten we ons een situatie voorstellen waarin onze entiteiten al zijn geladen in de persistentiecontext vóór onze operatie:

entiteitManager.getTransaction (). begin (); entiteitManager.persist (nieuwe game (1L, "Game 1")); entiteitManager.persist (nieuwe speler (1L, "Speler 1")); entiteitManager.getTransaction (). commit (); entiteitManager.getTransaction (). begin (); Game game1 = entityManager.getReference (Game.class, 1L); Player player1 = entityManager.find (Player.class, 1L); player1.setGame (game1); entiteitManager.persist (speler1); entiteitManager.getTransaction (). commit (); 

Het uitvoeren van de test laat zien dat alleen de updatevraag is uitgevoerd:

Slaapstand: update spelerset game_id = ?, name =? waar id =? 

In dat geval zouden we dat moeten opmerken we zien er geen SELECTEER queries, of we deze nu gebruiken vind() of getReference (). Dit komt omdat onze entiteiten in de cache worden opgeslagen in de cache op het eerste niveau van Hibernate.

Als resultaat, wanneer onze entiteiten worden opgeslagen in de cache van het eerste niveau van Hibernate, dan beide vind() en getReference () methoden werken identiek en raken onze database niet.

6. Verschillende PPV-implementaties

Als laatste herinnering: we moeten ons ervan bewust zijn dat het gedrag van de getReference () methode hangt af van de onderliggende persistentieleverancier.

Volgens de JPA 2-specificatie mag de persistentieprovider de EntityNotFoundException wanneer de getReference () methode wordt genoemd. Het kan dus anders zijn voor andere persistentieproviders en we kunnen tegenkomen EntityNotFoundException wanneer we gebruiken getReference ().

Niettemin, Hibernate volgt de specificatie voor getReference () standaard om indien mogelijk een database-rondreis op te slaan. Dienovereenkomstig genereert het geen uitzondering wanneer we entiteitsproxy's ophalen, zelfs als ze niet in de database bestaan.

Alternatief, Hibernate biedt een configuratie-eigenschap om een ​​eigenzinnige manier te bieden voor degenen die de JPA-specificatie willen volgen.

In dat geval kunnen we overwegen om de slaapstand.jpa.compliance.proxy eigendom aan waar:

Met deze instelling initialiseert Hibernate de entiteitproxy in elk geval, wat betekent dat deze een SELECTEER vraag zelfs wanneer we getReference ().

7. Conclusie

In deze zelfstudie hebben we enkele use-cases onderzocht die kunnen profiteren van de referentieproxy-objecten en hebben we geleerd hoe u deze kunt gebruiken EntityManager‘S getReference () methode in Slaapstand.

Zoals altijd zijn alle codevoorbeelden en meer testcases voor deze tutorial beschikbaar op GitHub.