MS Lesson15: Locking mechanism & Transaction propogation
1. Optimistic Read = Optimistic
Optimistic Read zamani row-a lock qoyulmur version ile idare olunur. Optimistic o demekdir ki, problem olmayacaq ve ya cox az olacaq.
package guru.springframework.cruddemo.service.impl;
import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.repository.AccountRepository;
import guru.springframework.cruddemo.service.AccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
@Override
@Transactional
public void transfer(Integer amount) {
System.out.println(Thread.currentThread().getName());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
Account fromAccount = accountRepository.findWithOptimisticLockById(1L).orElseThrow();
try {
Thread.sleep(6000);
} catch (Exception e) {
e.printStackTrace();
}
Account toAccount = accountRepository.findWithOptimisticLockById(2L).orElseThrow();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
}
}
@Lock(LockModeType.OPTIMISTIC)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithOptimisticLockById(@Param("id") Long id);
2. Optimistic Write = Optimistic force increment
Burada ise versiya mecburi artir
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithOptimisticForceIncrementById(@Param("id") Long id);
3. Pessimistic Read = lock in share mode (S lock)
@Lock(LockModeType.PESSIMISTIC_READ)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithPessimisticReadLockById(@Param("id") Long id);
Pessimisti Read - de deadlock bash vere biler.
4. Pessimistic Write = select for update (X lock)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithPessimisticWriteLockById(@Param("id") Long id);
1. A servisi transactional metodu var ve B servisindeki non transactional metodu cagiri:
package guru.springframework.cruddemo.controller;
import guru.springframework.cruddemo.service.impl.AccountServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequiredArgsConstructor
public class AccountController {
private final AccountServiceImpl accountServiceImpl;
@PostMapping("/transfer")
public void transfer() {
accountServiceImpl.transfer(30);
}
}
package guru.springframework.cruddemo.service.impl;
import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.repository.AccountRepository;
import guru.springframework.cruddemo.service.AccountService;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
private final TxProxyDemoService txProxyDemoService;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Integer amount) {
System.out.println(Thread.currentThread().getName());
Account fromAccount = accountRepository.findById(1L).get();
Account toAccount = accountRepository.findById(2L).get();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
txProxyDemoService.test();
}
private void foo() {
throw new RuntimeException("---------> xeta");
}
}
package guru.springframework.cruddemo.service.impl;
import guru.springframework.cruddemo.entity.TxProxyDemo;
import guru.springframework.cruddemo.repository.TxProxyDemoRepository;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class TxProxyDemoServiceImpl implements TxProxyDemoService {
private final TxProxyDemoRepository repository;
@Override
// @Transactional
public void test() {
System.out.println(Thread.currentThread().getName());
TxProxyDemo txProxyDemo = repository.findById(1L).get();
txProxyDemo.setContent("demo");
foo();
}
private void foo() {
throw new RuntimeException("------> Something went wrong");
}
}
Bu zaman transaction B servisindeki metoda da oturulur, orada davam edir ve tranzaksiya A servisindeki metodda bitir.
2. Required - bu @Transactional yazanda default secimdir.
A servisindeki @Transactional metoddan B servisindeki @Transactional metodu cagirirsa B servisindeki tranzaksiya yeni tranzaksiya acmir A ni davam eletdirir, yeni A nin tranzaksiyasi B ye oturulur.
Bu zaman her hansisa bir metodda Runtime exception bash verersen bu tranzaksiya vahid bir tranzaksiya kimi davranacaq ve butun emeliyyat (hem A hem B) rollback olacaq.
3. Requires_new - bu zaman yeni tranzaksiya serhedi teyin olunur. Bunu gormek ucun:
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
Qisa qayda: Requires_new neye gore var? Birincide xeta bash verse ikinciye tesir etmesin, bu qeder sade.
4. Supports - tranzaksiya varsa qatil, yoxdursa tranzaksiyasiz ishle.
Qisa qayda: yazsanda yazmasanda ferq etmir.
5. Not_supported - tranzaksiya varsa suspend et, tranzaksiya olmadan ishle.
package guru.springframework.cruddemo.service.impl;
import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.repository.AccountRepository;
import guru.springframework.cruddemo.service.AccountService;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
private final TxProxyDemoService txProxyDemoService;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Integer amount) {
System.out.println(Thread.currentThread().getName());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
Account fromAccount = accountRepository.findById(1L).get();
Account toAccount = accountRepository.findById(2L).get();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
txProxyDemoService.test();
// foo();
}
private void foo() {
throw new RuntimeException("---------> xeta");
}
}
package guru.springframework.cruddemo.service.impl;
import guru.springframework.cruddemo.entity.TxProxyDemo;
import guru.springframework.cruddemo.repository.TxProxyDemoRepository;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Slf4j
@Service
@RequiredArgsConstructor
public class TxProxyDemoServiceImpl implements TxProxyDemoService {
private final TxProxyDemoRepository repository;
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void test() {
System.out.println(Thread.currentThread().getName());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
TxProxyDemo txProxyDemo = repository.findById(1L).get();
txProxyDemo.setContent("demo");
// repository.save(txProxyDemo);
// foo();
}
private void foo() {
throw new RuntimeException("------> Something went wrong");
}
}
test() metodunda RuntimeException bash verse bu exceptionu cagirildigi metoda yoneldecek.
Ne zaman lazim ola biler: cox vaxt uzun ve ya yavash ishleyen ishler (mes. email, HTTP fayl, agir hesabat) ve ya bu addim aktiv DB transaction-da olmasin dediyimiz hallarda - esas transaction-u suspend edib hemin hisseni tx-siz ishletmek ucun.
6. Mandatory - mutleq aktiv tranzaksiya olmalidir, eks halda xeta atilacaq. open-in-view = true buna hec bir tesiri yoxdur, yalniz session aciq saxlayir, response controllere qayitdiqdan sonra session baglanir.
7. Never - hec vaxt tranzaksiya olmamalidir, varsa xeta atsin. Tranzaksiya yoxdursa normal tranzaksiyasiz (adi metod kimi) ishleyir.
8. Nested - daxili nested savepoint (JDBC savepoint; DataSource desteklemelidir). Spring-in yeni versiyasinda desteklemir, evezine : Required_new + try/catch bloku. Yeni B-de xeta olsa bele, A davam etsin.
Комментарии
Отправить комментарий