Transaktionsformering og isolering om foråret @ Transactional
1. Introduktion
I denne vejledning dækker vi @Transaktionel kommentar og dens isolation og formering indstillinger.
2. Hvad er? @Transaktionel?
Vi kan bruge @Transaktionel at indpakke en metode i en databasetransaktion.
Det giver os mulighed for at indstille formerings-, isolations-, timeout-, skrivebeskyttelses- og tilbageførselsbetingelser for vores transaktion. Vi kan også specificere transaktionsadministratoren.
2.1. @Transaktionel Implementeringsoplysninger
Spring opretter en proxy eller manipulerer klasse-byte-koden til at styre oprettelse, begåelse og tilbageførsel af transaktionen. I tilfælde af en proxy ignorerer Spring @Transaktionel i interne metodeopkald.
Kort sagt, hvis vi har en metode som callMethod og vi markerer det som @Transaktionel, Foråret ville pakke nogle transaktionsstyringskoder omkring påkaldelsen:@Transaktionel metode kaldet:
createTransactionIfNecessary (); prøv {callMethod (); commitTransactionAfterReturning (); } fange (undtagelse) {completeTransactionAfterThrowing (); kast undtagelse }
2.2. Sådan bruges @Transaktionel
Vi kan sætte kommentaren på definitioner af grænseflader, klasser eller direkte på metoder. De tilsidesætter hinanden i henhold til prioritetsrækkefølgen; fra laveste til højeste har vi: Interface, superklasse, klasse, grænseflademetode, superklassemetode og klassemetode.
Foråret anvender klassens annotering på alle offentlige metoder i denne klasse, som vi ikke kommenterede @Transaktionel .
Men hvis vi sætter kommentaren på en privat eller beskyttet metode, ignorerer Spring den uden en fejl.
Lad os starte med en interfaceeksempel:
@Transactional public interface TransferService {ugyldig overførsel (streng bruger1, streng bruger2, dobbelt val); }
Normalt anbefales det ikke at indstille @Transaktionel på grænsefladen. Det er dog acceptabelt i tilfælde som @Repository med Spring Data.
Vi kan sætte kommentaren på en klassedefinition for at tilsidesætte transaktionsindstillingen for grænsefladen / superklassen:
@Service @Transactional public class TransferServiceImpl implementerer TransferService {@Override public void transfer (String user1, String user2, double val) {// ...}}
Lad os nu tilsidesætte det ved at indstille kommentaren direkte på metoden:
@Transactional public void transfer (String user1, String user2, double val) {// ...}
3. Forplantning af transaktion
Formering definerer vores forretningslogik 's transaktionsgrænse. Spring formår at starte og pause en transaktion i henhold til vores formering indstilling.
Forårskald TransactionManager :: getTransaction at få eller oprette en transaktion i henhold til forplantningen. Det understøtter nogle af formeringer for alle typer TransactionManager, men der er et par af dem, der kun understøttes af specifikke implementeringer af TransactionManager.
Lad os nu gennemgå de forskellige formeringer, og hvordan de fungerer. PÅKRÆVET er standardformering. Spring kontrollerer, om der er en aktiv transaktion, så opretter den en ny, hvis der ikke var noget. Ellers føjes forretningslogikken til den aktuelt aktive transaktion: Også som PÅKRÆVET er standardformering, kan vi forenkle koden ved at slippe den: Lad os se pseudokoden for, hvordan oprettelse af transaktioner fungerer for PÅKRÆVET formering: Til BAKKER OP, Spring kontrollerer først, om der findes en aktiv transaktion. Hvis der findes en transaktion, vil den eksisterende transaktion blive brugt. Hvis der ikke er en transaktion, udføres den ikke-transaktionel: Lad os se transaktionens oprettelse pseudokode for BAKKER OP: Når udbredelsen er OBLIGATORISK, hvis der er en aktiv transaktion, vil den blive brugt. Hvis der ikke er en aktiv transaktion, kaster Spring en undtagelse: Og lad os igen se pseudokoden: For transaktionslogik med ALDRIG formering, Spring kaster en undtagelse, hvis der er en aktiv transaktion: Lad os se pseudokoden for, hvordan oprettelse af transaktioner fungerer for ALDRIG formering: Spring suspenderer først den aktuelle transaktion, hvis den findes, så udføres forretningslogikken uden en transaktion. Det JTATransactionManager understøtter ægte transaktionssuspension uden for kassen. Andre simulerer suspensionen ved at holde en henvisning til den eksisterende og derefter rydde den fra trådkonteksten Når udbredelsen er KRÆVER_NEW, Spring suspenderer den aktuelle transaktion, hvis den eksisterer, og opretter derefter en ny: Svarende til IKKE UNDERSTØTTET, vi har brug for JTATransactionManager for faktisk transaktionssuspension. Og pseudokoden ser sådan ud: Til NESTED forplantning, Spring kontrollerer, om der findes en transaktion, og hvis ja, markerer den et gemingspunkt. Dette betyder, at hvis vores forretningslogik udfører en undtagelse, så tilbageføres transaktioner til dette savepoint. Hvis der ikke er nogen aktiv transaktion, fungerer den som PÅKRÆVET . DataSourceTransactionManager understøtter denne formering uden for kassen. Også nogle implementeringer af JTATransactionManager kan støtte dette. JpaTransactionManager bakker op NESTED kun til JDBC-forbindelser. Men hvis vi sætter nestedTransactionAllowed flag til rigtigt, det fungerer også for JDBC-adgangskode i JPA-transaktioner, hvis vores JDBC-driver understøtter savepoints. Lad os endelig indstille formering til NESTED: Isolering er en af de almindelige syreegenskaber: Atomicitet, konsistens, isolering og holdbarhed. Isolation beskriver, hvordan ændringer anvendt af samtidige transaktioner er synlige for hinanden. Hvert isolationsniveau forhindrer nul eller flere samtidige bivirkninger på en transaktion: Vi kan indstille isolationsniveauet for en transaktion efter @Transactional :: isolation. Det har disse fem optællinger i foråret: STANDARD, READ_UNCOMMITTED, LÆS_KOMMITTERET, REPEATABLE_READ, SERIALISERBART. Standardisoleringsniveauet er STANDARD. Så når Spring opretter en ny transaktion, vil isolationsniveauet være standardisolationen af vores RDBMS. Derfor skal vi være forsigtige, hvis vi ændrer databasen. Vi bør også overveje tilfælde, hvor vi kalder en kæde af metoder med forskellig isolering. I den normale strømning gælder isolationen kun, når en ny transaktion oprettes. Så hvis vi af en eller anden grund ikke vil tillade, at en metode udføres i forskellige isolationer, er vi nødt til at indstille TransactionManager :: setValidateExistingTransaction til sandt. Derefter vil pseudokoden for validering af transaktion være: Lad os nu komme dybt i forskellige isolationsniveauer og deres virkninger. READ_UNCOMMITTED er det laveste isolationsniveau og giver mulighed for mest samtidig adgang. Som et resultat lider det af alle tre nævnte samtidige bivirkninger. Så en transaktion med denne isolation læser ikke-forpligtede data om andre samtidige transaktioner. Der kan også ske både ikke-gentagelige og fantomlæsninger. Således kan vi få et andet resultat ved genlæsning af en række eller genudførelse af en rækkeforespørgsel. Vi kan indstille isolation niveau for en metode eller klasse: Postgres understøtter ikke READ_UNCOMMITTED isolation og falder tilbage til READ_COMMITED i stedet. Oracle understøtter og tillader heller ikke READ_UNCOMMITTED. Det andet niveau af isolation, LÆS_COMMITTERET, forhindrer beskidte læser. Resten af samtidige bivirkninger kunne stadig ske. Så ikke-forpligtede ændringer i samtidige transaktioner har ingen indvirkning på os, men hvis en transaktion forpligter sine ændringer, kan vores resultat ændre sig ved at stille en forespørgsel. Her indstiller vi isolation niveau: LÆS_KOMMITTERET er standardniveauet med Postgres, SQL Server og Oracle. Det tredje niveau af isolation, REPEATABLE_READ, forhindrer beskidte og ikke-gentagelige læsninger. Så vi påvirkes ikke af uforpligtede ændringer i samtidige transaktioner. Når vi forespørger om en række, får vi heller ikke et andet resultat. Men i genudførelsen af rækkevidde-forespørgsler kan vi få nyligt tilføjede eller fjernede rækker. Desuden er det det lavest krævede niveau for at forhindre den mistede opdatering. Den mistede opdatering opstår, når to eller flere samtidige transaktioner læser og opdaterer den samme række. REPEATABLE_READ tillader slet ikke samtidig adgang til en række. Derfor kan den mistede opdatering ikke ske. Sådan indstilles isolation niveau for en metode: REPEATABLE_READ er standardniveauet i Mysql. Oracle understøtter ikke REPEATABLE_READ. SERIALISERBART er det højeste niveau af isolation. Det forhindrer alle nævnte samtidige bivirkninger, men kan føre til den laveste samtidige adgangshastighed, fordi den udfører samtidige opkald sekventielt. Med andre ord har samtidig udførelse af en gruppe af seriøse transaktioner det samme resultat som at udføre dem i serie. Lad os nu se, hvordan du indstiller SERIALISERBART som den isolation niveau: I denne vejledning undersøgte vi udbredelsesegenskaberne for @Transaktion i detaljer. Derefter lærte vi om samtidige bivirkninger og isolationsniveauer. Som altid kan du finde den komplette kode på GitHub.3.1. PÅKRÆVET Formering
@Transactional (propagation = Propagation.REQUIRED) public void requiredExample (String user) {// ...}
@Transactional public void requiredExample (String user) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } returnere eksisterende } returner createNewTransaction ();
3.2. BAKKER OP Formering
@Transactional (propagation = Propagation.SUPPORTS) public void supportsExample (String user) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } returnere eksisterende } returner tomTransaktion;
3.3. OBLIGATORISK Formering
@Transactional (propagation = Propagation.MANDATORY) public void obligatoryExample (String user) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } returnere eksisterende } smid IllegalTransactionStateException;
3.4. ALDRIG Formering
@Transactional (propagation = Propagation.NEVER) public void neverExample (String user) {// ...}
if (isExistingTransaction ()) {kast IllegalTransactionStateException; } returner tomTransaktion;
3.5. IKKE UNDERSTØTTET Formering
@Transactional (propagation = Propagation.NOT_SUPPORTED) public void notSupportedExample (String user) {// ...}
3.6. KRÆVER_NEW Formering
@Transactional (propagation = Propagation.REQUIRES_NEW) offentlig tomrum kræverNewExample (strengbruger) {// ...}
hvis (isExistingTransaction ()) {suspendere (eksisterende); prøv {return createNewTransaction (); } fange (undtagelse) {resumeAfterBeginException (); kast undtagelse }} returner createNewTransaction ();
3.7. NESTED Formering
@Transactional (propagation = Propagation.NESTED) public void nestedExample (String user) {// ...}
4. Transaktionsisolering
4.1. Isolationsstyring om foråret
if (isolationLevel! = ISOLATION_DEFAULT) {if (currentTransactionIsolationLevel ()! = isolationLevel) {throw IllegalTransactionStateException}}
4.2. READ_UNCOMMITTED Isolation
@Transactional (isolation = Isolation.READ_UNCOMMITTED) offentlig tomrumslog (strengbesked) {// ...}
4.3. LÆS_COMMITTERET Isolation
@Transactional (isolation = Isolation.READ_COMMITTED) offentlig ugyldig log (strengbesked) {// ...}
4.4. REPEATABLE_READ Isolation
@Transactional (isolation = Isolation.REPEATABLE_READ) offentlig tomrumslog (strengbesked) {// ...}
4.5. SERIALISERBART Isolation
@Transactional (isolation = Isolation.SERIALIZABLE) offentlig ugyldig log (streng besked) {// ...}
5. Konklusion