Optimistisk låsning i JPA

1. Introduktion

Når det kommer til virksomhedsapplikationer, er det afgørende at administrere samtidig adgang til en database korrekt. Dette betyder, at vi skal kunne håndtere flere transaktioner på en effektiv og vigtigst af alt fejlsikker måde.

Hvad mere er, skal vi sikre, at data forbliver konsistente mellem samtidige læsninger og opdateringer.

For at opnå det kan vi bruge optimistisk låsemekanisme leveret af Java Persistence API. Det fører til, at flere opdateringer foretaget på de samme data på samme tid ikke forstyrrer hinanden.

2. Forståelse af optimistisk låsning

For at bruge optimistisk låsning, vi skal have en enhed, der inkluderer en ejendom med @Version kommentar. Mens du bruger det, indeholder hver transaktion, der læser data, værdien af ​​versionsejendommen.

Før transaktionen ønsker at foretage en opdatering, kontrollerer den versionsejendommen igen.

Hvis værdien er ændret i mellemtiden, an OptimisticLockException kastes. Ellers forpligter transaktionen sig til opdateringen og øger en egenskab med værdiversion.

3. Pessimistisk låsning versus optimistisk låsning

Det er godt at vide, at JPA i modsætning til optimistisk låsning giver os pessimistisk låsning. Det er en anden mekanisme til håndtering af samtidig adgang til data.

Vi dækker pessimistisk låsning i en af ​​vores tidligere artikler - Pessimistisk låsning i JPA. Lad os finde ud af, hvad forskellen er, og hvordan vi kan drage fordel af hver type låsning.

Som vi har sagt før, optimistisk låsning er baseret på registrering af ændringer på enheder ved at kontrollere deres version-attribut. Hvis der finder sted samtidig opdatering, OptmisticLockException opstår. Derefter kan vi prøve at opdatere dataene igen.

Vi kan forestille os, at denne mekanisme er velegnet til applikationer, der læser meget mere end opdateringer eller sletninger. Desuden er det nyttigt i situationer, hvor enheder skal være løsrevet i nogen tid, og låse ikke kan holdes.

Tværtimod involverer pessimistisk låsemekanisme låsning af enheder på databaseniveau.

Hver transaktion kan erhverve en lås på data. Så længe det holder låsen, kan ingen transaktioner læse, slette eller foretage opdateringer af de låste data. Vi kan antage, at brug af pessimistisk låsning kan resultere i blokeringer. Det sikrer dog større integritet af data end optimistisk låsning.

4. Versionsattributter

Versionsattributter er egenskaber med @Version kommentar. De er nødvendige for at muliggøre optimistisk låsning. Lad os se en prøveenhedsklasse:

@Entity public class Student {@Id privat Lang id; privat strengnavn; privat streng efternavn; @Version privat heltal version; // getters og setters}

Der er flere regler, som vi skal følge, når vi erklærer versionsattributter:

  • hver enhedsklasse skal kun have en attribut til version
  • den skal placeres i den primære tabel for en enhed tilknyttet flere tabeller
  • version af attribut for version skal være en af ​​følgende: int, Heltal, lang, Lang, kort, Kort, java.sql.Tidsstempel

Vi skal vide, at vi kan hente en værdi af version-attributten via enhed, men vi må ikke opdatere eller øge den. Kun vedholdenhedsudbyderen kan gøre det, så data forbliver konsistente.

Det er værd at bemærke, at udholdenhedsudbydere er i stand til at understøtte optimistisk låsning for enheder, der ikke har versionattributter. Alligevel er det en god ide at altid medtage versionattributter, når du arbejder med optimistisk låsning.

Hvis vi forsøger at låse en enhed, der ikke indeholder en sådan attribut, og persistensudbyderen ikke understøtter den, vil det resultere i en PersitenceException.

5. Lås tilstande

JPA giver os to forskellige optimistiske låsemetoder (og to aliasser):

  • OPTIMISTISK - det opnår en optimistisk læselås for alle enheder, der indeholder en version-attribut
  • OPTIMISTIC_FORCE_INCREMENT - det opnår en optimistisk lås den samme som OPTIMISTISK og øger derudover versionens attributværdi
  • LÆS - det er et synonym for OPTIMISTISK
  • SKRIVE - det er et synonym for OPTIMISTIC_FORCE_INCREMENT

Vi kan finde alle de ovennævnte typer i LockModeType klasse.

5.1. OPTIMISTISK (LÆS)

Som vi allerede ved, OPTIMISTISK og LÆS låsemetoder er synonymer. JPA-specifikation anbefaler os dog at bruge OPTIMISTISK i nye applikationer.

Når vi anmoder om OPTIMISTISK låsemodus, vil en udholdenhedsudbyder forhindre vores data i beskidte læsninger såvel som ikke-gentagelige læsninger.

Kort sagt, det skal sikre, at enhver transaktion ikke foretager nogen ændringer på data, som en anden transaktion:

  • har opdateret eller slettet, men ikke forpligtet
  • har opdateret eller slettet i mellemtiden

5.2. OPTIMISTIC_INCREMENT (SKRIVE)

Det samme som tidligere, OPTIMISTIC_INCREMENT og SKRIVE er synonymer, men førstnævnte foretrækkes.

OPTIMISTIC_INCREMENT skal opfylde de samme betingelser som OPTIMISTISK låsemodus. Derudover øger den værdien af ​​en attribut til version. Det er dog ikke specificeret, om det skal gøres med det samme eller kan udsættes, indtil det begås eller skylles.

Det er værd at vide, at en persistensudbyder har lov til at levere OPTIMISTIC_INCREMENT funktionalitet når OPTIMISTISK der kræves låsefunktion.

6. Brug af optimistisk låsning

Vi skal huske, at for versionerede enheder er optimistisk låsning tilgængelig som standard. Alligevel er der flere måder at anmode om det eksplicit.

6.1. Finde

For at anmode om optimistisk låsning kan vi videregive det rette LockModeType som et argument for at finde metode til EntityManager:

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

6.2. Forespørgsel

En anden måde at aktivere låsning på er at bruge setLockMode metode til Forespørgsel objekt:

Forespørgselsforespørgsel = entityManager.createQuery ("fra studerende hvor id =: id"); query.setParameter ("id", studentId); query.setLockMode (LockModeType.OPTIMISTIC_INCREMENT); query.getResultList ()

6.3. Eksplicit låsning

Vi kan indstille en lås ved at ringe til EnitityManager's låse metode:

Studerende studerende = entityManager.find (Student.class, id); entityManager.lock (elev, LockModeType.OPTIMISTIC);

6.4. Opdater

Vi kan ringe til Opdater metode på samme måde som den foregående metode:

Studerende studerende = entityManager.find (Student.class, id); entityManager.refresh (elev, LockModeType.READ);

6.5. NamedQuery

Den sidste mulighed er at bruge @NamedQuery med lockMode ejendom:

@NamedQuery (navn = "optimisticLock", forespørgsel = "VÆLG s FRA Elev s HVOR s.id LIKE: id", lockMode = SKRIV)

7. OptimisticLockException

Når udholdenhedsudbyderen opdager optimistiske låsningskonflikter på enheder, kaster det OptimisticLockException. Vi skal være opmærksomme på, at den aktive transaktion altid er markeret som tilbageførsel på grund af undtagelsen.

Det er godt at vide, hvordan vi kan reagere på OptimisticLockException. Denne undtagelse indeholder bekvemt en henvisning til den modstridende enhed. Det er dog ikke obligatorisk for udholdenhedsudbyderen at levere det i enhver situation. Der er ingen garanti for, at objektet vil være tilgængeligt.

Der er dog en anbefalet måde at håndtere den beskrevne undtagelse på. Vi skal hente enheden igen ved at genindlæse eller opdatere. Fortrinsvis i en ny transaktion. Derefter kan vi prøve at opdatere det igen.

8. Konklusion

I denne vejledning blev vi fortrolige med et værktøj, der kan hjælpe os med at orkestrere samtidige transaktioner. Optimistisk låsning bruger versionsattributter inkluderet i enheder til at kontrollere samtidige ændringer på dem.

Derfor sikrer det, at opdateringer eller sletninger ikke overskrives eller mistes lydløst. I modsætning til pessimistisk låsning låser den ikke enheder på databaseniveau, og derfor er den ikke sårbar over for DB-blokeringer.

Vi har lært, at optimistisk låsning er aktiveret for versionerede enheder som standard. Der er dog flere måder at anmode om det eksplicit ved at bruge forskellige typer låsemodus.

En anden kendsgerning, vi skal huske, er at hver gang der er modstridende opdateringer af enheder, skal vi forvente en OptimisticLockException.

Endelig er kildekoden til denne vejledning tilgængelig på GitHub.