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. 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: Također kao POTREBAN je zadano širenje, možemo pojednostaviti kod ispuštanjem: Pogledajmo pseudo-kôd kako funkcionira stvaranje transakcija POTREBAN 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: Pogledajmo pseudo-kod stvaranja transakcije za PODRŠKE: Kad je širenje OBAVEZNO, ako postoji aktivna transakcija, tada će se koristiti. Ako ne postoji aktivna transakcija, tada Spring donosi iznimku: I da vidimo opet pseudo-kod: Za transakcijsku logiku s NIKADA širenja, Spring donosi iznimku ako postoji aktivna transakcija: Pogledajmo pseudo-kôd kako funkcionira stvaranje transakcija NIKADA razmnožavanje: Spring prvo suspendira trenutnu transakciju ako postoji, a zatim se poslovna logika izvršava bez transakcije. 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 Kad je širenje REQUIRES_NEW, Spring obustavlja trenutnu transakciju ako postoji, a zatim stvara novu: Slično NIJE PODRŽANO, trebamo JTATransactionManager za stvarnu obustavu transakcije. A pseudo-kod izgleda ovako: 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: 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: 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. 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: Ajmo sada duboko u različitim razinama izolacije i njihovim učincima. 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: 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. 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: READ_COMMITTED je zadana razina za Postgres, SQL Server i Oracle. 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: PONOVLJIVO_ČITANJE je zadana razina u Mysqlu. Oracle ne podržava PONOVLJIVO_ČITANJE. 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: 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.3.1. POTREBAN Razmnožavanje
@Transactional (propagation = Propagation.REQUIRED) potrebna je javna prazninaExample (Korisnik niza) {// ...}
@Transactional public void requiredExample (korisnik niza) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vratiti postojeće; } povratak createNewTransaction ();
3.2. PODRŠKE Razmnožavanje
@Transactional (propagation = Propagation.SUPPORTS) javna praznina supportsExample (Korisnik niza) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vratiti postojeće; } return emptyTransaction;
3.3. OBAVEZNO Razmnožavanje
@Transactional (propagation = Propagation.MANDATORY) javna praznina obaveznaExample (korisnik niza) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vratiti postojeće; } baciti IllegalTransactionStateException;
3.4. NIKADA Razmnožavanje
@Transactional (propagation = Propagation.NEVER) javna praznina neverExample (Korisnik niza) {// ...}
if (isExistingTransaction ()) {throw IllegalTransactionStateException; } return emptyTransaction;
3.5. NIJE PODRŽANO Razmnožavanje
@Transactional (propagation = Propagation.NOT_SUPPORTED) javna praznina notSupportedExample (Korisnik niza) {// ...}
3.6. REQUIRES_NEW Razmnožavanje
@Transactional (propagation = Propagation.REQUIRES_NEW) javna praznina zahtijevaNewExample (korisnik niza) {// ...}
if (isExistingTransaction ()) {suspend (postojeće); isprobajte {return createNewTransaction (); } ulov (iznimka) {resumeAfterBeginException (); baciti iznimku; }} return createNewTransaction ();
3.7. UGNJEDENO Razmnožavanje
@Transactional (propagation = Propagation.NESTED) javna praznina nestedExample (korisnik niza) {// ...}
4. Izolacija transakcije
4.1. Upravljanje izolacijom u proljeće
if (isolationLevel! = ISOLATION_DEFAULT) {if (currentTransactionIsolationLevel ()! = isolationLevel) {throw IllegalTransactionStateException}}
4.2. PROČITANO_NESPORUČENO Izolacija
@Transactional (isolation = Isolation.READ_UNCOMMITTED) javni void dnevnik (String poruka) {// ...}
4.3. READ_COMMITTED Izolacija
@Transactional (isolation = Isolation.READ_COMMITTED) javni void dnevnik (String poruka) {// ...}
4.4. PONOVLJIVO_ČITANJE Izolacija
@Transactional (isolation = Isolation.REPEATABLE_READ) javni void dnevnik (String poruka) {// ...}
4.5. SERIJALIZIRANO Izolacija
@Transactional (isolation = Isolation.SERIALIZABLE) javni void dnevnik (String poruka) {// ...}
5. Zaključak