Šírenie transakcie a izolácia na jar @ Transactional
1. Úvod
V tomto výučbe sa budeme venovať @ Transakčné anotácia a jej izolácia a rozmnožovanie nastavenie.
2. Čo je @ Transakčné?
Môžeme použiť @ Transakčné zabaliť metódu do databázovej transakcie.
Umožňuje nám nastaviť podmienky šírenia, izolácie, časového limitu, iba na čítanie a vrátenia zmien pre našu transakciu. Môžeme tiež určiť správcu transakcií.
2.1. @ Transakčné Podrobnosti implementácie
Spring vytvára proxy alebo manipuluje s bajtovým kódom triedy, aby riadil vytváranie, potvrdenie a vrátenie transakcie. V prípade proxy server Spring ignoruje @ Transakčné pri interných volaniach metód.
Jednoducho povedané, ak máme metódu ako callMethod a označíme to ako @Transactional, Jar by okolo vyvolania zabalila nejaký kód riadenia transakcií:@ Transakčné metóda zvaná:
createTransactionIfNecessary (); skus {callMethod (); commitTransactionAfterReturning (); } chytit (vynimka) {completeTransactionAfterThrowing (); hodiť výnimku; }
2.2. Ako použiť @ Transakčné
Anotáciu môžeme vložiť do definícií rozhraní, tried alebo priamo do metód. Prepíšu sa podľa poradia priorít; od najnižšej po najvyššiu máme: Rozhranie, nadtrieda, trieda, metóda rozhrania, metóda nadtriedy a metóda triedy.
Jar aplikuje anotáciu na úrovni triedy na všetky verejné metódy tejto triedy, ktoré sme anotovali @ Transakčné .
Ak však dáme anotáciu na súkromnú alebo chránenú metódu, Spring ju bez chyby ignoruje.
Začnime ukážkou rozhrania:
@Transactional public interface TransferService {void transfer (String user1, String user2, double val); }
Zvyčajne sa neodporúča nastavovať @ Transakčné na rozhraní. Je to však prijateľné pre prípady ako @Úložisko s údajmi z jari.
Môžeme vložiť anotáciu do definície triedy, aby sme prepísali nastavenie transakcie rozhrania / nadtriedy:
@Service @Transactional verejná trieda TransferServiceImpl implementuje TransferService {@Override public void transfer (String user1, String user2, double val) {// ...}}
Teraz to prepíšeme nastavením anotácie priamo na metódu:
@ Transakčný verejný prenos neplatnosti (reťazec user1, reťazec user2, dvojitá hodnota) {// ...}
3. Šírenie transakcie
Propagation definuje hranicu transakcie našej obchodnej logiky. Jar sa nám podarí spustiť a pozastaviť transakciu podľa našich rozmnožovanie nastavenie.
Jarné hovory TransactionManager :: getTransaction získať alebo vytvoriť transakciu podľa šírenia. Podporuje niektoré z propagácií pre všetky typy TransactionManager, ale existuje niekoľko z nich, ktoré sú podporované iba konkrétnymi implementáciami TransactionManager.
Poďme si teraz predstaviť rôzne propagácie a ich fungovanie. POŽADOVANÝ je predvolené šírenie. Jar skontroluje, či existuje aktívna transakcia, potom vytvorí novú, ak nič neexistovalo. V opačnom prípade sa obchodná logika pripojí k aktuálne aktívnej transakcii: Tiež ako POŽADOVANÝ je predvolené šírenie, môžeme kód zjednodušiť jeho vypustením: Pozrime sa na pseudokód, ako funguje vytváranie transakcií POŽADOVANÝ rozmnožovanie: Pre PODPORUJE„Jar najskôr skontroluje, či existuje aktívna transakcia. Ak transakcia existuje, použije sa existujúca transakcia. Ak neexistuje transakcia, vykoná sa bez transakcie: Pozrime sa na pseudokód vytvorenia transakcie pre PODPORUJE: Keď je šírenie POVINNÉ, ak existuje aktívna transakcia, potom sa použije. Ak nie je aktívna transakcia, potom Spring vyvolá výnimku: A opäť sa pozrieme na pseudokód: Pre transakčnú logiku s NIKDY propagácia, Spring hodí výnimku, ak existuje aktívna transakcia: Pozrime sa na pseudokód, ako funguje vytváranie transakcií NIKDY rozmnožovanie: Jar najskôr pozastaví aktuálnu transakciu, ak existuje, potom sa obchodná logika vykoná bez transakcie. The JTATransactionManager podporuje pozastavenie skutočných transakcií ihneď po vybalení z krabice. Iní simulujú pozastavenie podržaním odkazu na existujúce a následným vymazaním z kontextu vlákna Keď je šírenie REQUIRES_NEWSpring pozastaví aktuálnu transakciu, ak existuje, a potom vytvorí novú: Podobný NIE JE PODPOROVANÉ, potrebujeme JTATransactionManager za skutočné pozastavenie transakcie. A pseudokód vyzerá takto: Pre VNÚTORNÉ propagácia, Spring skontroluje, či existuje transakcia, potom ak áno, označí bod uloženia. To znamená, že ak vykonanie našej obchodnej logiky spôsobí výnimku, transakcie sa vrátia späť do tohto bodu uloženia. Ak nie je aktívna transakcia, funguje to ako POŽADOVANÝ . DataSourceTransactionManager podporuje toto šírenie ihneď po vybalení z krabice. Tiež niektoré implementácie JTATransactionManager môže toto podporovať. JpaTransactionManager podporuje VNÚTORNÉ iba pre pripojenia JDBC. Keby sme však nastavili nestedTransactionAllowed označiť pravda, funguje to aj pre prístupový kód JDBC v transakciách JPA, ak náš ovládač JDBC podporuje body uloženia. Nakoniec nastavíme rozmnožovanie do VNÚTORNÉ: Izolácia je jednou z bežných vlastností kyselín: atomicita, konzistencia, izolácia a trvanlivosť. Izolácia popisuje, ako sú vzájomne viditeľné zmeny aplikované pri súbežných transakciách. Každá úroveň izolácie zabraňuje nulovým alebo viacerým vedľajším účinkom súbežnosti na transakciu: Úroveň izolácie transakcie môžeme nastaviť pomocou @ Transactional :: isolation. Na jar má týchto päť zoznamov: VÝCHODNÉ, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZOVATEĽNÉ. Predvolená úroveň izolácie je VÝCHODNÉ. Takže keď Spring vytvorí novú transakciu, úroveň izolácie bude predvolenou izoláciou nášho RDBMS. Preto by sme mali byť pri zmene databázy opatrní. Mali by sme zvážiť aj prípady, keď voláme reťazec metód s odlišnou izoláciou. V normálnom toku sa izolácia použije iba vtedy, keď sa vytvorí nová transakcia. Ak teda z nejakého dôvodu nechceme umožniť, aby sa metóda vykonávala v inej izolácii, musíme to nastaviť TransactionManager :: setValidateExistingTransaction do pravdy. Potom bude pseudokód overenia transakcie: Poďme teraz hlboko do rôznych úrovní izolácie a ich účinkov. READ_UNCOMMITTED je najnižšia úroveň izolácie a umožňuje najväčší súčasný prístup. Vo výsledku trpí všetkými tromi spomenutými vedľajšími účinkami súbežnosti. Takže transakcia s touto izoláciou číta nezáväzné údaje iných súbežných transakcií. Môžu sa vyskytnúť aj neopakovateľné a fantómové čítania. Takto môžeme získať iný výsledok pri opätovnom načítaní riadku alebo opätovnom vykonaní dotazu na rozsah. Môžeme nastaviť izolácia úroveň pre metódu alebo triedu: Postgres nepodporuje READ_UNCOMMITTED izolácia a spadá späť do Namiesto toho READ_COMMITED. Spoločnosť Oracle tiež nepodporuje a nepovoľuje READ_UNCOMMITTED. Druhá úroveň izolácie, READ_COMMITTED, zabraňuje špinavému čítaniu. Zvyšok vedľajších účinkov súbežnosti sa ešte môže stať. Takže nezáväzné zmeny v súbežných transakciách na nás nemajú žiadny vplyv, ale ak sa transakcia zaviaže k zmenám, náš výsledok by sa mohol zmeniť opätovným dopytovaním. Tu sme nastavili izolácia úroveň: READ_COMMITTED je predvolená úroveň pre Postgres, SQL Server a Oracle. Tretia úroveň izolácie, REPEATABLE_READ, zabraňuje špinavému a neopakovateľnému čítaniu. Nie sú teda ovplyvnení nezáväznými zmenami v súbežných transakciách. Keď znova zadáme dopyt po riadku, nedostaneme iný výsledok. Ale pri opätovnom vykonávaní dotazov na rozsah môžeme získať novo pridané alebo odstránené riadky. Okrem toho je to najnižšia požadovaná úroveň, aby sa zabránilo stratenej aktualizácii. Stratená aktualizácia nastane, keď dve alebo viac súbežných transakcií číta a aktualizuje ten istý riadok. REPEATABLE_READ vôbec neumožňuje súčasný prístup k riadku. Stratená aktualizácia sa preto nemôže stať. Tu je postup, ako nastaviť izolácia úroveň pre metódu: REPEATABLE_READ je predvolená úroveň v MySQL. Spoločnosť Oracle nepodporuje REPEATABLE_READ. SERIALIZOVATEĽNÉ je najvyššia úroveň izolácie. Zabraňuje všetkým spomenutým vedľajším účinkom súbežnosti, ale môže viesť k najnižšej miere súbežného prístupu, pretože vykonáva súbežné volania postupne. Inými slovami, súčasné vykonávanie skupiny serializovateľných transakcií má rovnaký výsledok ako ich sériové vykonávanie. Teraz sa pozrime, ako nastaviť SERIALIZOVATEĽNÉ ako izolácia úroveň: V tomto tutoriáli sme preskúmali vlastnosť šírenia @ Transakcia podrobne. Potom sme sa dozvedeli o vedľajších účinkoch súbežnosti a úrovniach izolácie. Ako vždy, celý kód nájdete na GitHub.3.1. POŽADOVANÝ Propagácia
@Transactional (propagation = Propagation.REQUIRED) public void requiredExample (používateľ reťazca) {// ...}
@Transactional public void requiredExample (používateľ reťazca) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vrátiť existujúce; } návrat createNewTransaction ();
3.2. PODPORUJE Propagácia
@Transactional (propagation = Propagation.SUPPORTS) public void podporujeExample (používateľ reťazca) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vrátiť existujúce; } return emptyTransaction;
3.3. POVINNÉ Propagácia
@Transactional (propagation = Propagation.MANDATORY) public void requiredExample (používateľ reťazca) {// ...}
if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vrátiť existujúce; } hod IllegalTransactionStateException;
3.4. NIKDY Propagácia
@Transactional (propagation = Propagation.NEVER) public void neverExample (používateľ reťazca) {// ...}
if (isExistingTransaction ()) {throw IllegalTransactionStateException; } return emptyTransaction;
3.5. NIE JE PODPOROVANÉ Propagácia
@Transactional (propagation = Propagation.NOT_SUPPORTED) public void notSupportedExample (reťazec) {// ...}
3.6. REQUIRES_NEW Propagácia
@Transactional (propagation = Propagation.REQUIRES_NEW) public void requireNewExample (používateľ reťazca) {// ...}
if (isExistingTransaction ()) {pozastaviť (existujúce); try {return createNewTransaction (); } chytit (vynimka) {resumeAfterBeginException (); hodiť výnimku; }} vratit createNewTransaction ();
3.7. VNÚTORNÉ Propagácia
@Transactional (propagation = Propagation.NESTED) public void nestedExample (používateľ reťazca) {// ...}
4. Izolácia transakcie
4.1. Manažment izolácie na jar
if (isolationLevel! = ISOLATION_DEFAULT) {if (currentTransactionIsolationLevel ()! = isolationLevel) {hod IllegalTransactionStateException}}
4.2. READ_UNCOMMITTED Izolácia
@Transactional (isolation = Isolation.READ_UNCOMMITTED) verejný protokol neplatností (reťazcová správa) {// ...}
4.3. READ_COMMITTED Izolácia
@Transactional (isolation = Isolation.READ_COMMITTED) verejný protokol neplatností (reťazcová správa) {// ...}
4.4. REPEATABLE_READ Izolácia
@Transactional (isolation = Isolation.REPEATABLE_READ) verejný protokol neplatností (reťazcová správa) {// ...}
4.5. SERIALIZOVATEĽNÉ Izolácia
@Transactional (isolation = Isolation.SERIALIZABLE) public void log (reťazcová správa) {// ...}
5. Záver