Transaksjon forplantning og isolasjon på våren @ Transactional
1. Introduksjon
I denne opplæringen vil vi dekke @Transaksjonell kommentar og dens isolering og forplantning innstillinger.
2. Hva er? @Transaksjonell?
Vi kan bruke @Transaksjonell å pakke inn en metode i en databasetransaksjon.
Det lar oss sette forplantnings-, isolasjons-, timeout-, skrivebeskyttelses- og tilbakeføringsbetingelser for transaksjonen. Vi kan også spesifisere transaksjonssjefen.
2.1. @Transaksjonell Implementeringsdetaljer
Spring oppretter en proxy eller manipulerer klasse-byte-koden for å administrere opprettelse, forpliktelse og tilbakeføring av transaksjonen. I tilfelle fullmektig ignorerer Spring @Transaksjonell i interne metodesamtaler.
Enkelt sagt, hvis vi har en metode som callMethod og vi merker det som @Transaksjonell, Våren ville pakke inn noen transaksjonsstyringskoder rundt påkallelsen:@Transaksjonell metoden kalt:
createTransactionIfNecessary (); prøv {callMethod (); commitTransactionAfterReturning (); } fange (unntak) {completeTransactionAfterThrowing (); kaste unntak; }
2.2. Hvordan å bruke @Transaksjonell
Vi kan sette kommentaren på definisjoner av grensesnitt, klasser eller direkte på metoder. De overstyrer hverandre i henhold til prioritetsrekkefølgen; fra laveste til høyeste har vi: Grensesnitt, superklasse, klasse, grensesnittmetode, superklassemetode og klassemetode.
Våren bruker klassekommentar på alle offentlige metoder i denne klassen som vi ikke kommenterte @Transaksjonell .
Imidlertid, hvis vi legger kommentaren til en privat eller beskyttet metode, vil Spring ignorere den uten feil.
La oss starte med et grensesnitteksempel:
@Transactional public interface TransferService {void transfer (String user1, String user2, double val); }
Vanligvis anbefales det ikke å stille inn @Transaksjonell på grensesnittet. Det er imidlertid akseptabelt i tilfeller som @Oppbevaringssted med Spring Data.
Vi kan sette merknaden på en klassedefinisjon for å overstyre transaksjonsinnstillingen til grensesnittet / superklassen:
@Service @Transactional public class TransferServiceImpl implementerer TransferService {@Override public void transfer (String user1, String user2, double val) {// ...}}
La oss nå overstyre det ved å sette kommentaren direkte på metoden:
@Transactional public void transfer (String user1, String user2, double val) {// ...}
3. Transaksjon forplantning
Formering definerer vår forretningslogikk 's transaksjonsgrense. Våren klarer å starte og stoppe en transaksjon i henhold til vår forplantning omgivelser.
Våren kaller TransactionManager :: getTransaction for å få eller opprette en transaksjon i henhold til forplantningen. Den støtter noen av forplantningene for alle typer TransactionManager, men det er noen få av dem som bare støttes av spesifikke implementeringer av TransactionManager.
La oss nå gå gjennom de forskjellige forplantningene og hvordan de fungerer. PÅKREVET er standard forplantning. Våren sjekker om det er en aktiv transaksjon, så oppretter den en ny hvis ingenting eksisterte. Ellers føyes virksomhetslogikken til den nåværende aktive transaksjonen: Også som PÅKREVET er standard forplantning, kan vi forenkle koden ved å slippe den: La oss se pseudokoden for hvordan transaksjonsoppretting fungerer for PÅKREVET forplantning: Til STØTTER, Spring sjekker først om en aktiv transaksjon eksisterer. Hvis det eksisterer en transaksjon, vil den eksisterende transaksjonen bli brukt. Hvis det ikke er en transaksjon, blir den utført ikke-transaksjonell: La oss se pseudokoden for transaksjonsopprettelsen for STØTTER: Når forplantningen er PÅBUDT, BINDENDE, hvis det er en aktiv transaksjon, vil den bli brukt. Hvis det ikke er en aktiv transaksjon, kaster Spring et unntak: Og la oss igjen se pseudokoden: For transaksjonslogikk med ALDRI forplantning, Spring kaster et unntak hvis det er en aktiv transaksjon: La oss se pseudokoden for hvordan transaksjonsoppretting fungerer for ALDRI forplantning: Våren stanser først den nåværende transaksjonen hvis den eksisterer, og deretter utføres forretningslogikken uten en transaksjon. De JTATransactionManager støtter ekte transaksjonssuspensjon utenom boksen. Andre simulerer suspensjonen ved å holde en referanse til den eksisterende og deretter fjerne den fra trådkonteksten Når forplantningen er KRÆVER_NEW, Spring stanser gjeldende transaksjon hvis den eksisterer og oppretter en ny: Lik IKKE STØTTET, vi trenger JTATransactionManager for faktisk suspensjon av transaksjonen. Og pseudokoden ser slik ut: Til NESTED forplantning, Spring sjekker om en transaksjon eksisterer, og hvis ja, markerer den et lagringspunkt. Dette betyr at hvis vår forretningslogikkutførelse gir et unntak, så tilbakeføres transaksjoner til dette lagringspunktet. Hvis det ikke er noen aktiv transaksjon, fungerer det som PÅKREVET . DataSourceTransactionManager støtter denne forplantningen utenom boksen. Også noen implementeringer av JTATransactionManager kan støtte dette. JpaTransactionManager støtter NESTED bare for JDBC-tilkoblinger. Men hvis vi setter nestedTransactionAllowed flagg til ektefungerer det også for JDBC-tilgangskode i JPA-transaksjoner hvis vår JDBC-driver støtter lagringspunkter. Til slutt, la oss stille inn forplantning til NESTED: Isolering er en av de vanlige syreegenskapene: Atomisitet, konsistens, isolasjon og holdbarhet. Isolasjon beskriver hvordan endringer som brukes av samtidige transaksjoner er synlige for hverandre. Hvert isolasjonsnivå forhindrer null eller flere samtidige bivirkninger på en transaksjon: Vi kan sette isolasjonsnivået for en transaksjon etter @Transactional :: isolasjon. Den har disse fem oppregningene om våren: MISLIGHOLDE, READ_UNCOMMITTED, LES_KOMMITTERT, REPEATABLE_READ, SERIALISERbar. Standard isolasjonsnivå er MISLIGHOLDE. Så når Spring oppretter en ny transaksjon, vil isolasjonsnivået være standardisolasjonen av RDBMS. Derfor bør vi være forsiktige hvis vi endrer databasen. Vi bør også vurdere tilfeller når vi kaller en kjede av metoder med ulik isolasjon. I normal flyt gjelder isolasjonen bare når en ny transaksjon ble opprettet. Så hvis vi av en eller annen grunn ikke vil tillate at en metode utføres i annen isolasjon, må vi stille TransactionManager :: setValidateExistingTransaction til sant. Da vil pseudokoden for validering av transaksjonen være: La oss nå komme dypt i forskjellige isolasjonsnivåer og deres effekter. READ_UNCOMMITTED er det laveste isolasjonsnivået og gir mest mulig tilgang. Som et resultat lider det av alle de tre nevnte samtidige bivirkningene. Så en transaksjon med denne isolasjonen leser uforpliktende data om andre samtidige transaksjoner. Dessuten kan både ikke-repeterbare og fantomlesninger skje. Dermed kan vi få et annet resultat ved omlesing av en rad eller re-kjøring av en rekkeforespørsel. Vi kan stille inn isolering nivå for en metode eller klasse: Postgres støtter ikke READ_UNCOMMITTED isolasjon og faller tilbake til READ_COMMITED i stedet. Oracle støtter ikke og tillater ikke READ_UNCOMMITTED. Det andre nivået av isolasjon, LES_KOMMITTERT, forhindrer skitne avlesninger. Resten av samtidige bivirkninger kan fortsatt skje. Så uforpliktede endringer i samtidige transaksjoner har ingen innvirkning på oss, men hvis en transaksjon begår endringene, kan resultatet vårt endres ved å spørre på nytt. Her setter vi isolering nivå: LES_KOMMITTERT er standardnivået med Postgres, SQL Server og Oracle. Det tredje nivået av isolasjon, REPEATABLE_READ, forhindrer skitne og ikke-repeterbare lesninger. Så vi blir ikke berørt av uforpliktende endringer i samtidige transaksjoner. Også når vi spør etter en rad, får vi ikke et annet resultat. Men i re-kjøring av rekkevidde-spørringer, kan vi få nylig lagt til eller fjernet rader. Videre er det det laveste nivået som kreves for å forhindre den tapte oppdateringen. Den tapte oppdateringen oppstår når to eller flere samtidige transaksjoner leser og oppdaterer samme rad. REPEATABLE_READ tillater ikke i det hele tatt samtidig tilgang til en rad. Derfor kan den tapte oppdateringen ikke skje. Slik stiller du inn isolering nivå for en metode: REPEATABLE_READ er standardnivået i Mysql. Oracle støtter ikke REPEATABLE_READ. SERIALISERbar er det høyeste nivået av isolasjon. Det forhindrer alle nevnte samtidige bivirkninger, men kan føre til den laveste samtidige tilgangshastigheten fordi den utfører samtidige samtaler sekvensielt. Med andre ord, samtidig utførelse av en gruppe serierbare transaksjoner har samme resultat som å utføre dem i serie. La oss nå se hvordan du setter inn SERIALISERbar som isolering nivå: I denne opplæringen undersøkte vi forplantningsegenskapene til @Transaksjon i detalj. Etterpå lærte vi om samtidige bivirkninger og isolasjonsnivåer. Som alltid kan du finne den fullstendige koden på GitHub.3.1. PÅKREVET Formering
@Transactional (propagation = Propagation.REQUIRED) public void requiredExample (String user) {// ...}
@ Transaksjonelt offentlig tomrom kreves Eksempel (strengbruker) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } returner eksisterende; } returner createNewTransaction ();
3.2. STØTTER Formering
@Transactional (propagation = Propagation.SUPPORTS) public void supportsExample (String user) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } returner eksisterende; } returner tomTransaksjon;
3.3. PÅBUDT, BINDENDE Formering
@Transactional (propagation = Propagation.MANDATORY) public void obligatoryExample (String user) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } returner eksisterende; } kast IllegalTransactionStateException;
3.4. ALDRI Formering
@Transactional (propagation = Propagation.NEVER) public void neverExample (String user) {// ...}
if (isExistingTransaction ()) {kast IllegalTransactionStateException; } returner tomTransaksjon;
3.5. IKKE STØTTET Formering
@Transactional (propagation = Propagation.NOT_SUPPORTED) public void notSupportedExample (String user) {// ...}
3.6. KRÆVER_NEW Formering
@Transactional (propagation = Propagation.REQUIRES_NEW) offentlig tomrom kreverNewExample (strengbruker) {// ...}
hvis (isExistingTransaction ()) {suspendere (eksisterende); prøv {return createNewTransaction (); } fange (unntak) {resumeAfterBeginException (); kaste unntak; }} returner createNewTransaction ();
3.7. NESTED Formering
@Transactional (propagation = Propagation.NESTED) public void nestedExample (String user) {// ...}
4. Transaksjonsisolasjon
4.1. Isolasjonsledelse om våren
if (isolationLevel! = ISOLATION_DEFAULT) {if (currentTransactionIsolationLevel ()! = isolationLevel) {throw IllegalTransactionStateException}}
4.2. READ_UNCOMMITTED Isolering
@Transactional (isolation = Isolation.READ_UNCOMMITTED) offentlig ugyldig logg (strengmelding) {// ...}
4.3. LES_KOMMITTERT Isolering
@Transactional (isolation = Isolation.READ_COMMITTED) offentlig ugyldighetslogg (strengmelding) {// ...}
4.4. REPEATABLE_READ Isolering
@Transactional (isolation = Isolation.REPEATABLE_READ) offentlig ugyldig logg (strengmelding) {// ...}
4.5. SERIALISERbar Isolering
@Transactional (isolation = Isolation.SERIALIZABLE) offentlig ugyldig logg (strengmelding) {// ...}
5. Konklusjon