Tranzakciók terjesztése és elkülönítése tavasszal @Transactional

1. Bemutatkozás

Ebben az oktatóanyagban kitérünk a @ Tranzakció annotáció és annak elkülönítés és szaporítás beállítások.

2. Mi az @ Tranzakció?

Tudjuk használni @ Tranzakció metódus beágyazása egy adatbázis-tranzakcióba.

Lehetővé teszi számunkra, hogy tranzakciónkhoz terjesztési, elkülönítési, időtúllépési, csak olvasható és visszagörgetési feltételeket állítsunk be. Megadhatjuk a tranzakciókezelőt is.

2.1. @ Tranzakció A megvalósítás részletei

A Spring létrehoz egy proxyt, vagy manipulálja az osztály bájtkódját a tranzakció létrehozásának, véglegesítésének és visszagörgetésének kezelésére. Proxy esetén Spring figyelmen kívül hagyja @ Tranzakció belső módszeres hívásokban.

Egyszerűen fogalmazva, ha van ilyen módszerünk callMethod és úgy jelöljük @Transactional, Spring a tranzakciókezelési kódot körbefuttatja az invokáció köré:@ Tranzakció nevű módszer:

createTransactionIfNecessary (); próbáld ki a {callMethod (); kötelezettségTransactionAfterReturning (); } fogás (kivétel) {teljesTransactionAfterThrowing (); dobás kivétel; }

2.2. Hogyan kell használni @ Tranzakció

Helyezhetjük az annotációt az interfészek, osztályok definícióira vagy közvetlenül a módszerekre. A prioritási sorrend szerint felülírják egymást; a legalacsonyabbtól a legmagasabbig: Interface, superclass, class, interface method, superclass method és class method.

Spring alkalmazza az osztályszintű kommentárokat az osztály összes nyilvános módszerére, amelyekkel nem jegyzeteltünk @ Tranzakció .

Ha azonban a jegyzetet privát vagy védett módszerre helyezzük, Spring hiba nélkül figyelmen kívül hagyja.

Kezdjük egy interfész mintával:

@Transactional public interface TransferService {void transfer (String user1, String user2, double val); } 

Általában nem ajánlott beállítani a @ Tranzakció a felületen. Azonban elfogadható az olyan esetekben, mint @Raktár tavaszi adatokkal.

Helyezhetjük az annotációt egy osztálydefinícióra, hogy felülírjuk az interfész / szuperosztály tranzakciós beállításait:

@Service @Transactional public class A TransferServiceImpl megvalósítja a TransferService {@Orride public void transfer (String user1, String user2, double val) {// ...}}

Most írjuk felül azt, hogy a jegyzetet közvetlenül a módszerre állítjuk:

@Transactional public void transfer (String user1, String user2, double val) {// ...}

3. Tranzakció terjedése

A terjesztés meghatározza üzleti logikánk tranzakciós határait. Tavasznak sikerül elindítania és szüneteltetnie egy tranzakciót a mi szokásaink szerint szaporítás beállítás.

Tavaszi hívások TransactionManager :: getTransaction hogy a terjedés szerint tranzakciót szerezzen vagy hozzon létre. Támogatja a terjesztések némelyikét az összes típushoz TransactionManager, de van néhány közülük, amelyet csak a TransactionManager.

Most nézzük át a különböző terjedéseket és azok működését.

3.1. KÍVÁNT Szaporítás

KÍVÁNT az alapértelmezett terjedés. A tavasz ellenőrzi, hogy van-e aktív tranzakció, majd újat hoz létre, ha semmi sem létezett. Ellenkező esetben az üzleti logika az aktuálisan aktív tranzakcióhoz kapcsolódik:

@Transactional (propagation = Propagation.REQUIRED) public void requiredPélda (karakterlánc felhasználó) {// ...}

Szintén mint KÍVÁNT az alapértelmezett terjedés, egyszerűsíthetjük a kódot azáltal, hogy eldobjuk:

@Transactional public void requiredPélda (karakterlánc felhasználó) {// ...}

Lássuk a tranzakció létrehozásának ál kódját KÍVÁNT szaporítás:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } visszatérés meglévő; } return createNewTransaction ();

3.2. TÁMOGATÁSOK Szaporítás

Mert TÁMOGATÁSOK, Tavasz először ellenőrzi, hogy létezik-e aktív tranzakció. Ha létezik tranzakció, akkor a meglévő tranzakciót kell használni. Ha nincs tranzakció, akkor nem tranzakciós módon kerül végrehajtásra:

@Transactional (propagation = Propagation.SUPPORTS) public void supportExample (String user) {// ...}

Lássuk a tranzakció létrehozásának ál kódját TÁMOGATÁSOK:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } visszatérés meglévő; } return emptyTransaction;

3.3. KÖTELEZŐ Szaporítás

Amikor a terjedés KÖTELEZŐ, ha aktív tranzakció van, akkor az felhasználásra kerül. Ha nincs aktív tranzakció, akkor Spring kivételt hoz:

@Transactional (propagation = Propagation.MANDATORY) public void obligatePélda (karakterlánc felhasználó) {// ...}

És nézzük meg ismét az álkódot:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } visszatérés meglévő; } dobás IllegalTransactionStateException;

3.4. SOHA Szaporítás

A tranzakciós logikához SOHA terjedés, Spring kivételt vet, ha aktív tranzakció van:

@Transactional (propagation = Propagation.NEVER) public void neverExample (String user) {// ...}

Lássuk a tranzakció létrehozásának ál kódját SOHA szaporítás:

if (isExistingTransaction ()) {dobja illegálisTransactionStateException; } return emptyTransaction;

3.5. NEM TÁMOGATOTT Szaporítás

Spring először felfüggeszti az aktuális tranzakciót, ha létezik, akkor az üzleti logika tranzakció nélkül kerül végrehajtásra.

@Transactional (propagation = Propagation.NOT_SUPPORTED) public void notSupportedExample (String user) {// ...}

A JTATransactionManager támogatja a valódi tranzakciók felfüggesztését. Mások úgy szimulálják a felfüggesztést, hogy tartanak egy hivatkozást a meglévőre, majd kitisztítják azt a szál kontextusából

3.6. REQUIRES_NEW Szaporítás

Amikor a terjedés REQUIRES_NEW, Spring felfüggeszti az aktuális tranzakciót, ha létezik, majd létrehoz egy újat:

@Transactional (propagation = Propagation.REQUIRES_NEW) public void requiredNewExample (String user) {// ...}

Hasonló NEM TÁMOGATOTT, szükségünk van a JTATransactionManager a tényleges ügylet felfüggesztésére.

Az álkód pedig így néz ki:

if (isExistingTransaction ()) {felfüggeszt (meglévő); próbáld ki a {return createNewTransaction (); } catch (kivétel) {resumeAfterBeginException (); dobás kivétel; }} return createNewTransaction ();

3.7. FÉRFI Szaporítás

Mert FÉRFI terjedés, Spring ellenőrzi, hogy létezik-e tranzakció, akkor ha igen, akkor egy mentési pontot jelöl. Ez azt jelenti, hogy ha az üzleti logikánk végrehajtása kivételt vet, akkor a tranzakció visszaállítása ennek a mentési pontnak. Ha nincs aktív tranzakció, úgy működik, mint KÍVÁNT .

DataSourceTransactionManager támogatja ezt a terjesztést a dobozon kívül. Ezenkívül a JTATransactionManager támogathatja ezt.

JpaTransactionManager támogatja FÉRFI csak JDBC kapcsolatokhoz. Ha azonban beállítjuk nestedTransactionAllowed zászló to igaz, a JPA tranzakciók JDBC hozzáférési kódja esetén is működik, ha a JDBC illesztőprogramunk támogatja a mentési pontokat.

Végül állítsuk be a szaporítás nak nek FÉRFI:

@Transactional (propagation = Propagation.NESTED) public void nestedExample (String user) {// ...}

4. Tranzakciók elkülönítése

Az izolálás az egyik leggyakoribb ACID tulajdonság: atomosság, konzisztencia, izolálás és tartósság. Az izolálás leírja, hogy az egyidejű tranzakciók által alkalmazott változások hogyan láthatók egymás számára.

Minden elkülönítési szint megakadályozza a tranzakció nulla vagy több egyidejűségét:

  • Piszkos olvasmány: olvassa el egy párhuzamos tranzakció elkötelezett változását
  • Megismételhetetlen olvasmány: kap egy másik értéket egy sor újraolvasásakor, ha egy párhuzamos tranzakció frissíti ugyanazt a sort és elkötelezi magát
  • Phantom így szólt: kapjon különböző sorokat a tartománylekérdezés újbóli végrehajtása után, ha egy másik tranzakció hozzáad vagy eltávolít néhány sort a tartományban, és elkötelezi magát

A tranzakció elkülönítési szintjét beállíthatjuk @Transactional :: elszigeteltség. Ez az öt felsorolás van tavasszal: ALAPVETÉS, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SZERIALIZÁLHATÓ.

4.1. Az izoláció kezelése tavasszal

Az alapértelmezett elkülönítési szint: ALAPÉRTÉK. Tehát, amikor a Spring új tranzakciót hoz létre, az izolációs szint lesz az RDBMS alapértelmezett elkülönítése. Ezért óvatosnak kell lennünk, ha megváltoztatjuk az adatbázist.

Figyelembe kell vennünk azokat az eseteket is, amikor különböző elszigeteltségnek nevezzük a módszerek láncolatát. A normál folyamatban az elkülönítés csak új tranzakció létrehozásakor érvényes. Ha tehát valamilyen okból nem akarjuk megengedni, hogy egy módszer külön-külön izoláltan hajtson végre végrehajtást, akkor be kell állítanunk TransactionManager :: setValidateExistingTransaction igazra. Ekkor a tranzakció érvényesítésének álkódja a következő lesz:

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

Most mélyedjünk el a különböző elszigeteltségi szintekben és azok hatásaiban.

4.2. READ_UNCOMMITTED Elkülönítés

READ_UNCOMMITTED a legalacsonyabb szigetelési szint, és a legtöbb egyidejű hozzáférést teszi lehetővé.

Ennek eredményeként mindhárom említett párhuzamossági mellékhatásban szenved. Tehát egy ezzel az elkülönítéssel végrehajtott tranzakció beolvassa más párhuzamos tranzakciók lekötetlen adatait. Emellett előfordulhatnak megismételhetetlen és fantomolvasások is. Így más eredményt kaphatunk egy sor újbóli elolvasásakor vagy egy tartomány lekérdezés újra végrehajtásakor.

Beállíthatjuk a elkülönítés módszer vagy osztály szintje:

@Transactional (isolation = Isolation.READ_UNCOMMITTED) public void log (karakterlánc-üzenet) {// ...}

A Postgres nem támogatja READ_UNCOMMITTED elszigeteltség és visszaesik Helyette READ_COMMITED. Az Oracle szintén nem támogatja és nem engedélyezi READ_UNCOMMITTED.

4.3. READ_COMMITTED Elkülönítés

Az elszigeteltség második szintje, READ_COMMITTED, megakadályozza a piszkos olvasásokat.

A párhuzamossági mellékhatások többi része még mindig előfordulhat. Tehát a párhuzamos tranzakciók elkötelezett változásai nincsenek hatással ránk, de ha egy tranzakció végrehajtja a változásait, akkor eredményünk megváltozhat az új lekérdezéssel.

Itt állítottuk be a elkülönítés szint:

@Transactional (isolation = Isolation.READ_COMMITTED) public void napló (karakterlánc üzenet) {// ...}

READ_COMMITTED az alapértelmezett szint a Postgres, az SQL Server és az Oracle esetében.

4.4. REPEATABLE_READ Elkülönítés

Az elszigeteltség harmadik szintje, REPEATABLE_READ, megakadályozza a piszkos és megismételhetetlen olvasásokat. Tehát minket nem érintenek a párhuzamos tranzakciók nem kötelező változásai.

Továbbá, amikor újból lekérdezünk egy sort, akkor nem kapunk más eredményt. De a tartomány-lekérdezések újbóli végrehajtása során újonnan hozzáadott vagy eltávolított sorokat kaphatunk.

Sőt, ez a legalacsonyabb szükséges szint az elveszett frissítés megakadályozásához. Az elveszített frissítés akkor következik be, amikor két vagy több egyidejű tranzakció beolvassa és frissíti ugyanazt a sort. REPEATABLE_READ egyáltalán nem engedélyezi a sorhoz való egyidejű hozzáférést. Ezért az elveszett frissítés nem történhet meg.

Itt állíthatja be a elkülönítés módszer szintje:

@Transactional (isolation = Isolation.REPEATABLE_READ) public void napló (karakterlánc üzenet) {// ...}

REPEATABLE_READ az alapértelmezett szint a MySQL-ben. Az Oracle nem támogatja REPEATABLE_READ.

4.5. SZERIALIZÁLHATÓ Elkülönítés

SZERIALIZÁLHATÓ a legmagasabb szintű elszigeteltség. Megakadályozza az összes említett párhuzamossági mellékhatást, de a legalacsonyabb egyidejű hozzáférési arányhoz vezethet, mivel egymás után hajtja végre az egyidejű hívásokat.

Más szavakkal, a szerializálható tranzakciók csoportjának egyidejű végrehajtása ugyanolyan eredménnyel jár, mint soros végrehajtás.

Most nézzük meg, hogyan kell beállítani SZERIALIZÁLHATÓ mint a elkülönítés szint:

@Transactional (isolation = Isolation.SERIALIZABLE) public void napló (karakterlánc üzenet) {// ...}

5. Következtetés

Ebben az oktatóanyagban feltártuk a @Tranzakció részletesen. Utána megismerhettük a párhuzamosság mellékhatásait és az izolációs szinteket.

Mint mindig, a teljes kódot a GitHubon találja meg.