MS Lesson14: @Transactional
1. Arxa planda proxy nece implement olunur
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;
@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
@Override
@Transactional
public void transfer(Integer amount) {
Account fromAccount = accountRepository.findById(1L).get();
Account toAccount = accountRepository.findById(2L).get();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
}
}
arxa planda yaranan klas:
RuntimeException ve Error rollback olunur, lakin Exception rollbak olunmur.
Neye gore Spring bele qerar verib?
Bunun koku Java-nin exception felsefesine gedir.
* Checked Exception (Exception) - gozlenilen/biznes ssenarisi ola biler, cagiran teref bunu idare etmelidir.
* RuntimeException ve Error - programin normal axini pozulub, berba etmek tehlukelidir.
Niye bele dizayn edilib?
Kohne enterprice Java-da checked exceptionlar cox vaxt biznes neticesi kimi istifade olunurdu (mes: "limit ashildi", "stock yoxdur"). Her checked exception-u rollback etmek istenmirdi, bezen qismet deyishiklikler saxlanmali olurdu. Ona gore Spring "konservativ" yolu secib, yalniz sistem tipli xetalarda avtomatik rollback edir.
* Eger @Transactional olan metod daxilden cagirilarsa o zaman rollback olmayacaq.
package guru.springframework.cruddemo.controller;
import guru.springframework.cruddemo.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.support.AopUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequiredArgsConstructor
public class AccountController {
private final AccountService accountService;
@PostMapping("/transfer")
public void transfer(@RequestParam Integer amount) throws Exception {
log.info("AccountService bean class: {}", accountService.getClass().getName());
log.info("Is AOP proxy: {}", AopUtils.isAopProxy(accountService));
log.info("Is JDK dynamic proxy: {}", AopUtils.isJdkDynamicProxy(accountService));
log.info("Is CGLIB proxy: {}", AopUtils.isCglibProxy(accountService));
accountService.foo(amount);
}
}
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;
@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
@Override
@Transactional
public void transfer(Integer amount) {
Account fromAccount = accountRepository.findById(1L).get();
Account toAccount = accountRepository.findById(2L).get();
fromAccount.setAmount(fromAccount.getAmount() - amount);
accountRepository.save(fromAccount);
foo2();
toAccount.setAmount(toAccount.getAmount() + amount);
accountRepository.save(toAccount);
}
@Override
public void foo(Integer amount) {
transfer(amount);
}
private void foo2() {
throw new RuntimeException("Something went wrong");
}
}
*** Qizil qayda:
@Transactional yalniz cagirish Spring proxy uzerinden gelende ishleyir. Eyni class daxilinde this.method() ile cagirish proxy-ni bypass edir, transaction acilmir.
2. Lost update nedir?
Iki tranzaksiya eyni setri oxuyur ve her ikisi ayriliqda deyishir, sonra son yazan evvelkinin deyishikliklerini "ustunden yazir" ve evvelki update itir.
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;
@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository repository;
@Override
public void transfer(Integer amount) {
log.info("---> Start transfer: thread: {}", Thread.currentThread().getName());
Account from = repository.findById(1L).get();
log.info("---> From account {}, thread: {}", from.getName(), Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Account to = repository.findById(2L).get();
log.info("---> To account {}, thread: {}", to.getName(), Thread.currentThread().getName());
from.setAmount(from.getAmount() - amount);
log.info("---> Update account {}, thread: {}", from.getName(), Thread.currentThread().getName());
repository.save(from);
log.info("---> Saving account, thread: {}", Thread.currentThread().getName());
to.setAmount(to.getAmount() + amount);
log.info("---> Update account {}, thread: {}", to.getName(), Thread.currentThread().getName());
repository.save(to);
log.info("---> Saving account, thread: {}", Thread.currentThread().getName());
}
}
Комментарии
Отправить комментарий