Razmnožavanje i izolacija transakcija u proljeće @ Transakcijski

1. Uvod

U ovom ćemo uputstvu pokriti @Transational bilješka i njezina izolacija i razmnožavanje postavke.

2. Što je @ Transakcijski?

Možemo koristiti @Transational za umotavanje metode u transakciju baze podataka.

Omogućuje nam postavljanje uvjeta širenja, izolacije, vremenskog ograničenja, samo za čitanje i vraćanja za našu transakciju. Također, možemo odrediti upravitelja transakcija.

2.1. @Transational Pojedinosti o provedbi

Spring stvara proxy ili manipulira bajt-kodom klase za upravljanje stvaranjem, urezivanjem i vraćanjem transakcije. U slučaju proxyja, Spring ignorira @Transational u internim pozivima metode.

Jednostavno rečeno, ako imamo metodu poput callMethod a mi ga označavamo kao @Transactional, Spring bi omotao neki kod za upravljanje transakcijama oko poziva:@Transational metoda koja se naziva:

createTransactionIfNecessary (); isprobajte {callMethod (); commitTransactionAfterReturning (); } ulov (iznimka) {completeTransactionAfterThrowing (); baciti iznimku; }

2.2. Kako koristiti @Transational

Bilješku možemo staviti na definicije sučelja, klase ili izravno na metode. Oni se nadjačavaju prema redoslijedu prioriteta; od najnižeg do najvišeg imamo: Sučelje, superklasu, klasu, metodu sučelja, metodu superklase i metodu klase.

Spring primjenjuje napomenu na razini razreda na sve javne metode ove klase s kojima nismo dodavali napomene @Transational .

Međutim, ako napomenu stavimo na privatnu ili zaštićenu metodu, Spring će je ignorirati bez pogreške.

Počnimo s uzorkom sučelja:

@ Transakcijsko javno sučelje TransferService {void transfer (String user1, String user2, double val); } 

Obično se ne preporučuje postavljanje @Transational na sučelju. Međutim, prihvatljivo je za slučajeve poput @ Repozitorij s Spring Data.

Bilješku možemo staviti u definiciju klase kako bismo nadjačali postavku transakcije sučelja / superklase:

@Service @Transactional javna klasa TransferServiceImpl implementira TransferService {@Override javni void transfer (String user1, String user2, double val) {// ...}}

Sada ga poništimo postavljanjem napomene izravno na metodu:

@Transakcijski javni prijenos praznina (String user1, String user2, double val) {// ...}

3. širenje transakcija

Propagacija definira granicu transakcija naše poslovne logike. Spring uspijeva pokrenuti i zaustaviti transakciju prema našoj razmnožavanje postavljanje.

Proljetni pozivi TransactionManager :: getTransaction da biste dobili ili stvorili transakciju prema širenju. Podržava neka razmnožavanja za sve vrste TransactionManager, ali postoji nekoliko njih koji su podržani samo određenim implementacijama TransactionManager.

Sada ćemo proći kroz različita širenja i kako oni djeluju.

3.1. POTREBAN Razmnožavanje

POTREBAN je zadano širenje. Spring provjerava postoji li aktivna transakcija, a zatim kreira novu ako ništa nije postojalo. Inače, poslovna logika dodaje se trenutno aktivnoj transakciji:

@Transactional (propagation = Propagation.REQUIRED) potrebna je javna prazninaExample (Korisnik niza) {// ...}

Također kao POTREBAN je zadano širenje, možemo pojednostaviti kod ispuštanjem:

@Transactional public void requiredExample (korisnik niza) {// ...}

Pogledajmo pseudo-kôd kako funkcionira stvaranje transakcija POTREBAN razmnožavanje:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vratiti postojeće; } povratak createNewTransaction ();

3.2. PODRŠKE Razmnožavanje

Za PODRŠKE, Spring prvo provjerava postoji li aktivna transakcija. Ako transakcija postoji, tada će se upotrijebiti postojeća transakcija. Ako nema transakcije, izvršava se netransakcijski:

@Transactional (propagation = Propagation.SUPPORTS) javna praznina supportsExample (Korisnik niza) {// ...}

Pogledajmo pseudo-kod stvaranja transakcije za PODRŠKE:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vratiti postojeće; } return emptyTransaction;

3.3. OBAVEZNO Razmnožavanje

Kad je širenje OBAVEZNO, ako postoji aktivna transakcija, tada će se koristiti. Ako ne postoji aktivna transakcija, tada Spring donosi iznimku:

@Transactional (propagation = Propagation.MANDATORY) javna praznina obaveznaExample (korisnik niza) {// ...}

I da vidimo opet pseudo-kod:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vratiti postojeće; } baciti IllegalTransactionStateException;

3.4. NIKADA Razmnožavanje

Za transakcijsku logiku s NIKADA širenja, Spring donosi iznimku ako postoji aktivna transakcija:

@Transactional (propagation = Propagation.NEVER) javna praznina neverExample (Korisnik niza) {// ...}

Pogledajmo pseudo-kôd kako funkcionira stvaranje transakcija NIKADA razmnožavanje:

if (isExistingTransaction ()) {throw IllegalTransactionStateException; } return emptyTransaction;

3.5. NIJE PODRŽANO Razmnožavanje

Spring prvo suspendira trenutnu transakciju ako postoji, a zatim se poslovna logika izvršava bez transakcije.

@Transactional (propagation = Propagation.NOT_SUPPORTED) javna praznina notSupportedExample (Korisnik niza) {// ...}

The JTATransactionManager podržava stvarnu obustavu transakcije odmah. Drugi simuliraju suspenziju držeći referencu na postojeću i zatim je uklanjajući iz konteksta niti

3.6. REQUIRES_NEW Razmnožavanje

Kad je širenje REQUIRES_NEW, Spring obustavlja trenutnu transakciju ako postoji, a zatim stvara novu:

@Transactional (propagation = Propagation.REQUIRES_NEW) javna praznina zahtijevaNewExample (korisnik niza) {// ...}

Slično NIJE PODRŽANO, trebamo JTATransactionManager za stvarnu obustavu transakcije.

A pseudo-kod izgleda ovako:

if (isExistingTransaction ()) {suspend (postojeće); isprobajte {return createNewTransaction (); } ulov (iznimka) {resumeAfterBeginException (); baciti iznimku; }} return createNewTransaction ();

3.7. UGNJEDENO Razmnožavanje

Za UGNJEDENO širenje, Spring provjerava postoji li transakcija, a ako da, označava točku spremanja. To znači da ako naše izvršavanje poslovne logike izuzme izuzetak, onda će se vraćanje transakcija izvršiti na ovu točku spremanja. Ako nema aktivne transakcije, funkcionira kao POTREBAN .

DataSourceTransactionManager podržava ovo širenje izravno iz kutije. Također, neke implementacije JTATransactionManager može to podržati.

JpaTransactionManager podupire UGNJEDENO samo za JDBC veze. Međutim, ako postavimo nestedTransactionAllowed zastava do pravi, također radi za JDBC pristupni kod u JPA transakcijama ako naš JDBC pokretački program podržava točke spremanja.

Napokon, postavimo razmnožavanje do UGNJEDENO:

@Transactional (propagation = Propagation.NESTED) javna praznina nestedExample (korisnik niza) {// ...}

4. Izolacija transakcije

Izolacija je jedno od uobičajenih svojstava KISELINE: Atomicitet, konzistencija, izolacija i trajnost. Izolacija opisuje kako su promjene primijenjene istodobnim transakcijama vidljive jedna drugoj.

Svaka razina izolacije sprječava nula ili više istodobnih nuspojava na transakciju:

  • Prljavo čitanje: pročitati neobaveženu promjenu istodobne transakcije
  • Neponovljivo čitanje: dobivanje različite vrijednosti ponovnim čitanjem retka ako istodobna transakcija ažurira isti redak i počini se
  • Fantomsko čitanje: dobiti različite retke nakon ponovnog izvršavanja upita o rasponu ako druga transakcija doda ili ukloni neke retke u rasponu i počini

Razinu izolacije transakcije možemo postaviti pomoću @Transactional :: izolacija. U proljeće ima ovih pet nabrajanja: ZADANO, PROČITANO_NESPORUČENO, READ_COMMITTED, PONOVLJIVO_ČITANJE, SERIJALIZIRANO.

4.1. Upravljanje izolacijom u proljeće

Zadana razina izolacije je ZADANO. Dakle, kada Spring stvori novu transakciju, razina izolacije bit će zadana izolacija našeg RDBMS-a. Stoga bismo trebali biti oprezni ako mijenjamo bazu podataka.

Također bismo trebali razmotriti slučajeve kada nazivamo lanac metoda s različitom izolacijom. U normalnom toku izolacija se primjenjuje samo kada se kreira nova transakcija. Stoga, ako iz bilo kojeg razloga ne želimo dopustiti da se metoda izvršava u različitoj izolaciji, moramo postaviti TransactionManager :: setValidateExistingTransaction istinitom. Tada će pseudo-kod provjere valjanosti transakcije biti:

if (isolationLevel! = ISOLATION_DEFAULT) {if (currentTransactionIsolationLevel ()! = isolationLevel) {throw IllegalTransactionStateException}}

Ajmo sada duboko u različitim razinama izolacije i njihovim učincima.

4.2. PROČITANO_NESPORUČENO Izolacija

PROČITANO_NESPORUČENO je najniža razina izolacije i omogućuje najs istodobniji pristup.

Kao rezultat, pati od sve tri spomenute nuspojave istodobnosti. Dakle, transakcija s ovom izolacijom čita nezauzete podatke drugih istodobnih transakcija. Također se mogu dogoditi i neponovljiva i fantomska čitanja. Tako možemo dobiti drugačiji rezultat ponovnim čitanjem reda ili ponovnim izvršavanjem upita o rasponu.

Možemo postaviti izolacija razina za metodu ili klasu:

@Transactional (isolation = Isolation.READ_UNCOMMITTED) javni void dnevnik (String poruka) {// ...}

Postgres ne podržava PROČITANO_NESPORUČENO izolacija i pada natrag u Umjesto toga READ_COMMITED. Također, Oracle ne podržava i dopušta PROČITANO_NEMOŽENO.

4.3. READ_COMMITTED Izolacija

Druga razina izolacije, READ_COMMITTED, sprečava prljava čitanja.

Ostali popratni nuspojave i dalje bi se mogli dogoditi. Dakle, neprihvaćene promjene u istodobnim transakcijama nemaju utjecaja na nas, ali ako transakcija izvrši svoje promjene, naš bi se rezultat mogao promijeniti ponovnim postavljanjem upita.

Ovdje smo postavili izolacija razina:

@Transactional (isolation = Isolation.READ_COMMITTED) javni void dnevnik (String poruka) {// ...}

READ_COMMITTED je zadana razina za Postgres, SQL Server i Oracle.

4.4. PONOVLJIVO_ČITANJE Izolacija

Treća razina izolacije, PONOVLJIVO_ČITANJE, sprječava prljava i neponovljiva čitanja. Dakle, na nas ne utječu neograničene promjene u istodobnim transakcijama.

Također, kada ponovno postavljamo upit za red, ne dobivamo drugačiji rezultat. Ali u ponovnom izvršavanju upita o rasponu, možemo dobiti novo dodane ili uklonjene retke.

Štoviše, to je najniža potrebna razina kako bi se spriječilo izgubljeno ažuriranje. Izgubljeno ažuriranje događa se kada dvije ili više istodobnih transakcija čitaju i ažuriraju isti redak. PONOVLJIVO_ČITANJE uopće ne dopušta istodobni pristup redu. Stoga se izgubljeno ažuriranje ne može dogoditi.

Evo kako postaviti izolacija razina za metodu:

@Transactional (isolation = Isolation.REPEATABLE_READ) javni void dnevnik (String poruka) {// ...}

PONOVLJIVO_ČITANJE je zadana razina u Mysqlu. Oracle ne podržava PONOVLJIVO_ČITANJE.

4.5. SERIJALIZIRANO Izolacija

SERIJALIZIRANO je najviši stupanj izolacije. Sprječava sve spomenute istovremene nuspojave, ali može dovesti do najniže stope istodobnog pristupa jer se istovremeno izvršavaju istodobni pozivi.

Drugim riječima, istodobno izvršavanje grupe serializiranih transakcija ima isti rezultat kao i njihovo izvršavanje u seriji.

Sada da vidimo kako postaviti SERIJALIZIRANO kao izolacija razina:

@Transactional (isolation = Isolation.SERIALIZABLE) javni void dnevnik (String poruka) {// ...}

5. Zaključak

U ovom uputstvu istražili smo svojstvo širenja @Transakcija detaljno. Poslije smo saznali o istodobnim nuspojavama i razinama izolacije.

Kao i uvijek, cjeloviti kod možete pronaći na GitHubu.