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. 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: Szintén mint KÍVÁNT az alapértelmezett terjedés, egyszerűsíthetjük a kódot azáltal, hogy eldobjuk: Lássuk a tranzakció létrehozásának ál kódját KÍVÁNT 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: Lássuk a tranzakció létrehozásának ál kódját TÁMOGATÁSOK: 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: És nézzük meg ismét az álkódot: A tranzakciós logikához SOHA terjedés, Spring kivételt vet, ha aktív tranzakció van: Lássuk a tranzakció létrehozásának ál kódját SOHA 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. 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 Amikor a terjedés REQUIRES_NEW, Spring felfüggeszti az aktuális tranzakciót, ha létezik, majd létrehoz egy újat: 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: 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: 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: 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Ó. 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: Most mélyedjünk el a különböző elszigeteltségi szintekben és azok hatásaiban. 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: 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. 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: READ_COMMITTED az alapértelmezett szint a Postgres, az SQL Server és az Oracle esetében. 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: REPEATABLE_READ az alapértelmezett szint a MySQL-ben. Az Oracle nem támogatja REPEATABLE_READ. 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: 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.3.1. KÍVÁNT Szaporítás
@Transactional (propagation = Propagation.REQUIRED) public void requiredPélda (karakterlánc felhasználó) {// ...}
@Transactional public void requiredPélda (karakterlánc felhasználó) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } visszatérés meglévő; } return createNewTransaction ();
3.2. TÁMOGATÁSOK Szaporítás
@Transactional (propagation = Propagation.SUPPORTS) public void supportExample (String user) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } visszatérés meglévő; } return emptyTransaction;
3.3. KÖTELEZŐ Szaporítás
@Transactional (propagation = Propagation.MANDATORY) public void obligatePélda (karakterlánc felhasználó) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } visszatérés meglévő; } dobás IllegalTransactionStateException;
3.4. SOHA Szaporítás
@Transactional (propagation = Propagation.NEVER) public void neverExample (String user) {// ...}
if (isExistingTransaction ()) {dobja illegálisTransactionStateException; } return emptyTransaction;
3.5. NEM TÁMOGATOTT Szaporítás
@Transactional (propagation = Propagation.NOT_SUPPORTED) public void notSupportedExample (String user) {// ...}
3.6. REQUIRES_NEW Szaporítás
@Transactional (propagation = Propagation.REQUIRES_NEW) public void requiredNewExample (String user) {// ...}
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
@Transactional (propagation = Propagation.NESTED) public void nestedExample (String user) {// ...}
4. Tranzakciók elkülönítése
4.1. Az izoláció kezelése tavasszal
if (isolationLevel! = ISOLATION_DEFAULT) {if (currentTransactionIsolationLevel ()! = isolationLevel) {dob IllegalTransactionStateException}}
4.2. READ_UNCOMMITTED Elkülönítés
@Transactional (isolation = Isolation.READ_UNCOMMITTED) public void log (karakterlánc-üzenet) {// ...}
4.3. READ_COMMITTED Elkülönítés
@Transactional (isolation = Isolation.READ_COMMITTED) public void napló (karakterlánc üzenet) {// ...}
4.4. REPEATABLE_READ Elkülönítés
@Transactional (isolation = Isolation.REPEATABLE_READ) public void napló (karakterlánc üzenet) {// ...}
4.5. SZERIALIZÁLHATÓ Elkülönítés
@Transactional (isolation = Isolation.SERIALIZABLE) public void napló (karakterlánc üzenet) {// ...}
5. Következtetés