Pessimistische vergrendeling in de PPV

1. Overzicht

Er zijn tal van situaties waarin we gegevens uit een database willen halen. Soms willen we het voor onszelf vergrendelen voor verdere verwerking, zodat niemand anders onze acties kan onderbreken.

We kunnen twee controlemechanismen voor gelijktijdigheid bedenken die ons in staat stellen om dat te doen: het juiste transactie-isolatieniveau instellen of een vergrendeling instellen op gegevens die we op dit moment nodig hebben.

De transactie-isolatie is gedefinieerd voor databaseverbindingen. We kunnen het configureren om de verschillende mate van vergrendelingsgegevens te behouden.

Echter, het isolatieniveau wordt ingesteld zodra de verbinding tot stand is gebracht en het beïnvloedt elke uitspraak binnen dat verband. Gelukkig kunnen we pessimistische vergrendeling gebruiken die databasemechanismen gebruikt om meer gedetailleerde exclusieve toegang tot de gegevens te reserveren.

We kunnen een pessimistisch slot gebruiken om ervoor te zorgen dat geen andere transacties gereserveerde gegevens kunnen wijzigen of verwijderen.

Er zijn twee soorten sloten die we kunnen behouden: een exclusief slot en een gedeeld slot. We kunnen gegevens lezen maar niet schrijven als iemand anders een gedeeld slot heeft. Om de gereserveerde gegevens te wijzigen of te verwijderen, hebben we een exclusieve vergrendeling nodig.

We kunnen exclusieve sloten kopen met ‘SELECTEER ... VOOR UPDATE‘Verklaringen.

2. Vergrendelmodi

De JPA-specificatie definieert drie pessimistische vergrendelingsmodi die we gaan bespreken:

  • PESSIMISTIC_READ - stelt ons in staat om een ​​gedeelde vergrendeling te verkrijgen en te voorkomen dat de gegevens worden bijgewerkt of verwijderd
  • PESSIMISTIC_WRITE - stelt ons in staat om een ​​exclusieve vergrendeling te verkrijgen en te voorkomen dat de gegevens worden gelezen, bijgewerkt of verwijderd
  • PESSIMISTIC_FORCE_INCREMENT - werkt als PESSIMISTIC_WRITE en het verhoogt bovendien een versieattribuut van een entiteit met versiebeheer

Ze zijn allemaal statische leden van de LockModeType class en laat transacties een databasevergrendeling verkrijgen. Ze worden allemaal bewaard totdat de transactie vastlegt of terugdraait.

Het is vermeldenswaard dat we slechts één slot per keer kunnen verkrijgen. Als het onmogelijk is a PersistenceException wordt gegooid.

2.1. PESSIMISTIC_READ

Wanneer we alleen gegevens willen lezen en geen vuile leesbewerkingen willen tegenkomen, kunnen we PESSIMISTIC_READ (gedeeld slot). We kunnen echter geen updates of verwijderingen uitvoeren.

Het komt wel eens voor dat de database die we gebruiken de PESSIMISTIC_READ lock, dus het is mogelijk dat we de PESSIMISTIC_WRITE lock in plaats daarvan.

2.2. PESSIMISTIC_WRITE

Elke transactie die gegevens moet vergrendelen en er wijzigingen in moet aanbrengen, moet de extensie PESSIMISTIC_WRITE slot. Volgens de JPA specificatie, holding PESSIMISTIC_WRITE lock voorkomt dat andere transacties de gegevens lezen, bijwerken of verwijderen.

Houd er rekening mee dat sommige databasesystemen multi-versie gelijktijdigheidscontrole implementeren waarmee lezers gegevens kunnen ophalen die al zijn geblokkeerd.

2.3. PESSIMISTIC_FORCE_INCREMENT

Dit slot werkt op dezelfde manier als PESSIMISTIC_WRITE, maar het werd geïntroduceerd om samen te werken met entiteiten met versiebeheer - entiteiten die een attribuut hebben dat is geannoteerd met @Versie.

Eventuele updates van entiteiten met versiebeheer kunnen worden voorafgegaan door het verkrijgen van het PESSIMISTIC_FORCE_INCREMENT slot. Het verkrijgen van dat slot resulteert in het bijwerken van de versiekolom.

Het is aan een persistentieprovider om te bepalen of deze ondersteuning biedt PESSIMISTIC_FORCE_INCREMENT voor entiteiten zonder versiebeheer of niet. Als dat niet het geval is, wordt de PersistanceException.

2.4. Uitzonderingen

Het is goed om te weten welke uitzondering kan optreden bij het werken met pessimistische vergrendeling. JPA specificatie biedt verschillende soorten uitzonderingen:

  • PessimisticLockException - geeft aan dat het verkrijgen van een vergrendeling of het omzetten van een gedeelde naar exclusieve vergrendeling mislukt en resulteert in een rollback op transactieniveau
  • LockTimeoutException - geeft aan dat het verkrijgen van een vergrendeling of het omzetten van een gedeelde vergrendeling naar een exclusieve time-out en resulteert in een rollback op statement-niveau
  • PersistanceException - geeft aan dat er een persistentieprobleem is opgetreden. PersistanceException en zijn subtypen, behalve NoResultException, NonUniqueResultException,LockTimeoutException, en QueryTimeoutException, markeert de actieve transactie die moet worden teruggedraaid.

3. Pessimistische sloten gebruiken

Er zijn een paar mogelijke manieren om een ​​pessimistische vergrendeling te configureren voor een enkel record of een groep records. Laten we eens kijken hoe we het in JPA kunnen doen.

3.1. Vind

Het is waarschijnlijk de meest eenvoudige manier. Het is genoeg om een LockModeType object als parameter voor het vind methode:

entityManager.find (Student.class, studentId, LockModeType.PESSIMISTIC_READ);

3.2. Vraag

Bovendien kunnen we een Vraag object en bel het setLockMode setter met een vergrendelingsmodus als parameter:

Queryquery = entityManager.createQuery ("van Student where studentId =: studentId"); query.setParameter ("studentId", studentId); query.setLockMode (LockModeType.PESSIMISTIC_WRITE); query.getResultList ()

3.3. Expliciete vergrendeling

Het is ook mogelijk om de resultaten die zijn opgehaald met de zoekmethode handmatig te vergrendelen:

Student resultStudent = entityManager.find (Student.class, studentId); entiteitManager.lock (resultStudent, LockModeType.PESSIMISTIC_WRITE);

3.4. Vernieuwen

Als we de staat van de entiteit willen overschrijven met de vernieuwen methode, kunnen we ook een slot instellen:

Student resultStudent = entityManager.find (Student.class, studentId); entiteitManager.refresh (resultStudent, LockModeType.PESSIMISTIC_FORCE_INCREMENT);

3.5. NamedQuery

@NamedQuery annotatie stelt ons in staat om ook een vergrendelingsmodus in te stellen:

@NamedQuery (name = "lockStudent", query = "SELECTEER s VAN Student s WAAR s.id LIKE: studentId", lockMode = PESSIMISTIC_READ)

4. Bereik vergrendelen

De parameter Vergrendelingsbereik definieert hoe om te gaan met vergrendelingsrelaties van de vergrendelde entiteit. Het is mogelijk om een ​​vergrendeling te verkrijgen voor alleen een enkele entiteit die in een query is gedefinieerd, of om daarnaast de relaties ervan te blokkeren.

Om de scope te configureren die we kunnen gebruiken PessimisticLockScope opsomming. Het bevat twee waarden: NORMAAL en UITGEBREID.

We kunnen het bereik instellen door een parameter ‘javax.persistance.lock.scope‘Met PessimisticLockScope waarde als argument voor de juiste methode van EntityManager, Vraag, TypedQuery of NamedQuery:

Kaarteigenschappen = nieuwe HashMap (); map.put ("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); entityManager.find (Student.class, 1L, LockModeType.PESSIMISTIC_WRITE, eigenschappen); 

4.1. PessimisticLockScope.NORMAL

We moeten weten dat de PessimisticLockScope.NORMAL is het standaardbereik. Met deze vergrendelingsscope vergrendelen we de entiteit zelf. Bij gebruik met samengevoegde overerving vergrendelt het ook de voorouders.

Laten we eens kijken naar de voorbeeldcode met twee entiteiten:

@Entity @Inheritance (strategy = InheritanceType.JOINED) openbare klasse Persoon {@Id privé Lange id; private String naam; private String achternaam; // getters and setters} @ Entity public class Werknemer verlengt Persoon {privé BigDecimal salaris; // getters en setters}

Als we een slot willen krijgen op de Werknemer, kunnen we de SQL query die zich uitstrekt over deze twee entiteiten:

SELECTEER t0.ID, t0.DTYPE, t0.LASTNAME, t0.NAME, t1.ID, t1 SALARIS VAN PERSOON t0, WERKNEMER t1 WAAR ((t0.ID =?) EN ((t1.ID = t0.ID) EN (t0.DTYPE =?))) VOOR UPDATE

4.2. PessimisticLockScope.UITGEBREID

De UITGEBREID scope omvat dezelfde functionaliteit als NORMAAL. Daarnaast, het is in staat om gerelateerde entiteiten in een samenvoegtabel te blokkeren.

Simpel gezegd, het werkt met entiteiten die zijn geannoteerd met @ElementCollection of @Een op een, @Een te veel etc. met @JoinTable.

Laten we de voorbeeldcode bekijken met de @ElementCollection annotatie:

@Entity openbare klasse Klant {@Id privé Lange klant-ID; private String naam; private String achternaam; @ElementCollection @CollectionTable (name = "customer_address") privélijst addressList; // getters and setters} @Embeddable public class Address {private String country; particuliere String stad; // getters en setters}

Laten we enkele vragen analyseren bij het zoeken naar het Klant entiteit:

SELECTEER CUSTOMERID, ACHTERNAAM, NAAM VAN KLANT WAAR (CUSTOMERID =?) VOOR UPDATE SELECTEER STAD, LAND, Klant_CUSTOMERID VAN klant_adres WAAR (Klant_CUSTOMERID =?) VOOR UPDATE

We kunnen zien dat er twee zijn ‘VOOR UPDATE‘Queries die zowel een rij in de klantentabel als een rij in de join-tafel vergrendelen.

Een ander interessant feit waar we ons bewust van moeten zijn, is dat niet alle persistentieproviders lock-scopes ondersteunen.

5. Lock Timeout instellen

Naast het instellen van vergrendelingsbereiken, kunnen we een andere vergrendelingsparameter aanpassen - time-out. De time-outwaarde is het aantal milliseconden dat we willen wachten op het verkrijgen van een vergrendeling tot het LockTimeoutException treedt op.

We kunnen de waarde van de time-out op dezelfde manier wijzigen om bereiken te vergrendelen, door de eigenschap ‘javax.persistence.lock.timeout ' met het juiste aantal milliseconden.

Het is ook mogelijk om 'niet wachten'-vergrendeling op te geven door de time-outwaarde op nul te zetten. We moeten echter in gedachten houden dat er databasestuurprogramma's zijn die ondersteunt het instellen van een time-outwaarde op deze manier niet.

Kaarteigenschappen = nieuwe HashMap (); map.put ("javax.persistence.lock.timeout", 1000L); entityManager.find (Student.class, 1L, LockModeType.PESSIMISTIC_READ, eigenschappen);

6. Conclusie

Wanneer het instellen van het juiste isolatieniveau niet voldoende is om met gelijktijdige transacties om te gaan, geeft JPA ons een pessimistische vergrendeling. Het stelt ons in staat om verschillende transacties te isoleren en te orkestreren, zodat ze niet tegelijkertijd toegang hebben tot dezelfde bron.

Om dat te bereiken, kunnen we kiezen tussen besproken soorten vergrendelingen en bijgevolg parameters zoals hun bereik of time-outs wijzigen.

Aan de andere kant moeten we niet vergeten dat het begrijpen van databasevergrendelingen net zo belangrijk is als het begrijpen van de mechanismen van onderliggende databasesystemen. Het is ook belangrijk om in gedachten te houden dat het gedrag van pessimistische sloten afhangt van de persistentieprovider waarmee we samenwerken.

Ten slotte is de broncode van deze tutorial beschikbaar op GitHub voor slaapstand en voor EclipseLink.


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