Ší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.

3.1. POŽADOVANÝ Propagácia

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:

@Transactional (propagation = Propagation.REQUIRED) public void requiredExample (používateľ reťazca) {// ...}

Tiež ako POŽADOVANÝ je predvolené šírenie, môžeme kód zjednodušiť jeho vypustením:

@Transactional public void requiredExample (používateľ reťazca) {// ...}

Pozrime sa na pseudokód, ako funguje vytváranie transakcií POŽADOVANÝ rozmnožovanie:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vrátiť existujúce; } návrat createNewTransaction ();

3.2. PODPORUJE Propagácia

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:

@Transactional (propagation = Propagation.SUPPORTS) public void podporujeExample (používateľ reťazca) {// ...}

Pozrime sa na pseudokód vytvorenia transakcie pre PODPORUJE:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vrátiť existujúce; } return emptyTransaction;

3.3. POVINNÉ Propagácia

Keď je šírenie POVINNÉ, ak existuje aktívna transakcia, potom sa použije. Ak nie je aktívna transakcia, potom Spring vyvolá výnimku:

@Transactional (propagation = Propagation.MANDATORY) public void requiredExample (používateľ reťazca) {// ...}

A opäť sa pozrieme na pseudokód:

if (isExistingTransaction ()) {if (isValidateExistingTransaction ()) {validateExisitingAndThrowExceptionIfNotValid (); } vrátiť existujúce; } hod IllegalTransactionStateException;

3.4. NIKDY Propagácia

Pre transakčnú logiku s NIKDY propagácia, Spring hodí výnimku, ak existuje aktívna transakcia:

@Transactional (propagation = Propagation.NEVER) public void neverExample (používateľ reťazca) {// ...}

Pozrime sa na pseudokód, ako funguje vytváranie transakcií NIKDY rozmnožovanie:

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

3.5. NIE JE PODPOROVANÉ Propagácia

Jar najskôr pozastaví aktuálnu transakciu, ak existuje, potom sa obchodná logika vykoná bez transakcie.

@Transactional (propagation = Propagation.NOT_SUPPORTED) public void notSupportedExample (reťazec) {// ...}

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

3.6. REQUIRES_NEW Propagácia

Keď je šírenie REQUIRES_NEWSpring pozastaví aktuálnu transakciu, ak existuje, a potom vytvorí novú:

@Transactional (propagation = Propagation.REQUIRES_NEW) public void requireNewExample (používateľ reťazca) {// ...}

Podobný NIE JE PODPOROVANÉ, potrebujeme JTATransactionManager za skutočné pozastavenie transakcie.

A pseudokód vyzerá takto:

if (isExistingTransaction ()) {pozastaviť (existujúce); try {return createNewTransaction (); } chytit (vynimka) {resumeAfterBeginException (); hodiť výnimku; }} vratit createNewTransaction ();

3.7. VNÚTORNÉ Propagácia

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É:

@Transactional (propagation = Propagation.NESTED) public void nestedExample (používateľ reťazca) {// ...}

4. Izolácia transakcie

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:

  • Špinavé čítanie: prečítajte si nezáväznú zmenu súbežnej transakcie
  • Neopakovateľné čítanie: získať inú hodnotu pri opätovnom načítaní riadku, ak súbežná transakcia aktualizuje rovnaký riadok a zaviaže sa
  • Fantómové čítanie: získať rôzne riadky po opätovnom vykonaní dotazu na rozsah, ak iná transakcia pridá alebo odstráni niektoré riadky v rozsahu a zaviaže sa

Ú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É.

4.1. Manažment izolácie na jar

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:

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

Poďme teraz hlboko do rôznych úrovní izolácie a ich účinkov.

4.2. READ_UNCOMMITTED Izolácia

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:

@Transactional (isolation = Isolation.READ_UNCOMMITTED) verejný protokol neplatností (reťazcová správa) {// ...}

Postgres nepodporuje READ_UNCOMMITTED izolácia a spadá späť do Namiesto toho READ_COMMITED. Spoločnosť Oracle tiež nepodporuje a nepovoľuje READ_UNCOMMITTED.

4.3. READ_COMMITTED Izolácia

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ň:

@Transactional (isolation = Isolation.READ_COMMITTED) verejný protokol neplatností (reťazcová správa) {// ...}

READ_COMMITTED je predvolená úroveň pre Postgres, SQL Server a Oracle.

4.4. REPEATABLE_READ Izolácia

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:

@Transactional (isolation = Isolation.REPEATABLE_READ) verejný protokol neplatností (reťazcová správa) {// ...}

REPEATABLE_READ je predvolená úroveň v MySQL. Spoločnosť Oracle nepodporuje REPEATABLE_READ.

4.5. SERIALIZOVATEĽNÉ Izolácia

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ň:

@Transactional (isolation = Isolation.SERIALIZABLE) public void log (reťazcová správa) {// ...}

5. Záver

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.