IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Spring - Transakce v Javě

V předchozí lekci, REST API v Java Spring Boot - Detail uživatele a zabezpečení, jsme dokončili přihlášení uživatele k našemu API a nadefinovali si přístupová pravidla k jednotlivým akcím.

Transakce je posloupnost databázových příkazů, které se provedou v jednom běhu. Pokud se nějaký z nich neprovede (např. neprojde přes integritní omezení), neprovede se žádný z příkazů (všechno nebo nic).

Základní pojmy

  • BEGIN - započne transakci
  • COMMIT - všechny příkazy se vykonají postupně
  • ROOLBACK - nastala chyba v nějakém příkazu. Data se vrátí do původního stavu (před začátkem transakce).

ACID

Pravidla, co musí každá transakce splňovat.

  1. Atomicity (atomicita) - transakce proběhne buď celá nebo neproběhne vůbec
  2. Consistency (konzistence) - při zapsání (vykonání příkazů) není porušeno žádné integritní omezení
  3. Isolation (izolovanost) - transakce je černá skříňka, operace se navzájem v rámci více transakcí neovlivní
  4. Durability (trvalost) – jednou uložené změny nezmizí a jsou uložené natrvalo

Spring

Transaction Manager

Řídí práci s transakcemi. V praxi se s ním setkáte jen výjimečně. Jde spíše o pochopení principu fungování.

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

Nejspíše si říkáte, kde je metoda na zahájení transakce (begin). Metoda getTransaction je velmi specifická. Pokud transakce existuje vrátí ji, pokud ne vytvoří ji. Transakci zde reprezentuje třída TransactionStatus.

Nastavení ve Spring

Je potřeba zavést transakční manager do aplikačního kontextu:

<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<tx:annotation-driven ... nám zajistí, že Spring bude číst anotace (@Transactional). Spring zařídí všechnu špinavou práci za nás (vytvoření proxy tříd...).

Nejspíše si povšimnete, že je potřeba transactionManager předat dataSource. DataSource slouží pro připojení aplikace k databázi. Sedlácky řečeno v něm nadefinujete adresu, jméno a heslo a on už se postará o připojení. Více se můžete dočíst v dokumentaci k dataSource

Bez anotaci

Pokud znáte princip fungování, nepřekvapí Vás nečekané situace. Při použití nástroje pro integritní testování jsme zjistili, že mechanismus pomocí anotací nefunguje podle očekávání. Vytvořili jsme tedy abstraktní třídu, která měla na starost řízení transakcí.

Integrační test

Testuje integraci (funkčnost propojení) více komponent dohromady.

Příklad: "Mám data v nějakém stavu. Provedu posloupnost příkazu napříč aplikací. Data se změnili podle očekávání.

public abstract class AbstractTransactionTest {
  @Autowired
  private DataSourceTransactionManager manager;

  private TransactionStatus transactionStatus;

  @Before
  public final void beforeTest() {
    //typ transakce
    int propagationBehavior = DefaultTransactionDefinition.PROPAGATION_REQUIRED;
    // vytvori inicialni stav transakce
    DefaultTransactionDefinition initStatus = new DefaultTransactionDefinition(propagationBehavior);
    // aktivuje transakci
    transactionStatus = transactionManager.getTransaction(initStatus);
  }

  @After
  public final void afterTest() {
    manager.rollback(transactionStatus);
  }

Princip je jednoduchý. Na začátku testu se zahájí transakce. Po dokončení testu se provede rollback.

Možná si říkáte, proč se provádí rollback... Jak jsme se dočetli v předchozích tutoriálech, testy by se neměli navzájem ovlivňovat. Pokud by se dostala data do databáze, mohlo by dojít ke komplikacím. Jiný test by mohl začínat s daty ve stavu, který neočekává.

Anotace

@Transactional(pro­pagation ...) V praxi Vám tato anotace bohatě postačí na veškerou práci s transakcemi.

  • Propagation.RE­QUIRED - DEFAULT. Pokud existuje transakce, je přejatá. Pokud neexistuje, je vytvořena nová.
  • Propagation.MAN­DATORY - Pokud existuje transakce, je přejatá. Pokud neexistuje, vyhodí se výjimka.
  • Propagation.NOT_SUP­PORTED - Pozastaví běžící transakci a vykoná kód netransakčně.
  • Propagation.SUP­PORTED - Pokud existuje transakce, je přejatá. Pokud neexistuje, pustí se kód netrasakčně.
  • Propagation.RE­QUIRES_NEW - Pokud existuje transakce, je pozastavena a vytvoří se nová. Pokud neexistuje, vytvoří se nová.
  • Propagation.NEVER - Pokud existuje transakce, vyhodí výjimku. Jinak se vykoná netransakčně.
  • Propagation.NES­TED - Pokud existuje transakce, pustí novou (běží 2 paralelně). Pokud neexistuje, vytvoří se.

Jednoduché použití

Část kódu, která má běžet v transakci je označená danou anotací (@Transactional).

@Transactional
public class CarService {
  public void myTransactionMethod() {}
}

Jak to funguje?

Princip si ukážeme na imaginárním příkladu.

Anotace nám zajístí, že se nad danou třídou vytvoří proxy objekt (proxy pattern).

Daná metoda, co má běžet v transakci, je zaobalena do podobného bloku:

TransactionalManager txManager;

public void myTransactionMethod(Object o) {
  try {
    TransactionStatus  status = txManger.getTransaction(definition);
    myTransactionMethod();
    txManager.commit(status);
  } catch (Exception e) {
    txManager.roolback(status);
  }

}

pozn. Příklad vysvětluje jen princip fungování, v reálu je to složitější.

Kdykoli poté někdo zavolá danou třídu, zavolá se tento proxy objekt a vykoná se daný kód v transakci.

Pokud vše proběhne v naší metodě v pořádku (nevyhodí se výjimka), transakce manager provede commit transakce. Všechny příkazy se provedou nad databází.

Pokud v naší metodě dojde k chybě (např. kontrola duplicity záznamu vyhodí výjimku), manager provede roolback. Všechny příkazy po startu transakce se zahodí.

Architektura: Kde řídit transakci?

Neboli kde použít @Transactional. Pokud máte třívrstvou architekturu aplikace, transakce se obvykle řídí na vrstvě služby.

3-vrstvá architektura - REST API ve Spring Boot - Filmová databáze

Třívrstvá architektura aplikace:

  • Controller - slouží pro komunikaci s front-end. S tím co vidí uživatel.
  • Service - vrstva, kde probíhají hlavní výpočty aplikace (zde se používá @Transactional)
  • DAO - vrstva pro přístup do repository (databáze)

 

Předchozí článek
REST API v Java Spring Boot - Detail uživatele a zabezpečení
Všechny články v sekci
REST API ve Spring Boot - Filmová databáze
Článek pro vás napsal Petr Kunčar
Avatar
Uživatelské hodnocení:
1 hlasů
Nejlepší práce je taková, která vás baví. Nejlepší manželka je taková, co vás chápe. Nejlepší rodina je taková, co vás podporuje. Nejlepší relax je v přírodě. Nejlepší, co pro svět můžeš udělat, je řešit problémy rychle a elegantně.
Aktivity