Ответ
Основной способ — использование аннотации @Transactional. Она управляет транзакциями автоматически, избавляя от необходимости писать шаблонный код.
Пример сервиса:
@Service
public class TransferService {
@Autowired
private AccountRepository repository;
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account from = repository.findById(fromId).orElseThrow();
Account to = repository.findById(toId).orElseThrow();
from.debit(amount);
to.credit(amount);
repository.saveAll(List.of(from, to));
}
}
Как это работает:
- Перед вызовом метода помеченного
@Transactional, Spring открывает транзакцию (если еще не активна). - При успешном завершении метода транзакция фиксируется (commit).
- При выбрасывании непроверяемого исключения (
RuntimeExceptionили его подкласса) транзакция откатывается (rollback).
Ключевые параметры аннотации:
propagation— определяет поведение при наличии уже активной транзакции (например,REQUIRED,REQUIRES_NEW).isolation— уровень изоляции (например,READ_COMMITTED).readOnly— оптимизация для операций только на чтение.rollbackFor/noRollbackFor— уточнение, для каких исключений выполнять откат.
Требования: Для работы необходимо включить управление транзакциями в конфигурации с помощью @EnableTransactionManagement.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши транзакции, блядь! Весь этот шаблонный код, который писать заебись как неохота. Так вот же, сука, волшебная палочка — @Transactional. Всё, пиздец, больше ни о чём не думаешь, она сама всё сделает, как мама родная.
Смотри, как это просто, ёпта:
@Service
public class TransferService {
@Autowired
private AccountRepository repository;
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account from = repository.findById(fromId).orElseThrow();
Account to = repository.findById(toId).orElseThrow();
from.debit(amount);
to.credit(amount);
repository.saveAll(List.of(from, to));
}
}
А работает-то оно как, блядь?
- Ты только заходишь в метод, а Спринг уже, сука, транзакцию открыл. Если, конечно, её ещё нет — он не дурак, второй раз открывать.
- Всё прошло гладко, без косяков? Отлично, транзакция коммитится, и все твои изменения летят в базу.
- А если вылетело какое-нибудь непроверяемое исключение, типа
RuntimeException? Всё, пиздец, приехали — транзакция откатывается нахуй, как будто ничего и не было. Красота!
Ну и там ещё, блядь, параметров до овердохуища можно накрутить:
propagation— это когда одна транзакция уже есть, а ты лезешь со своей. Тут можно сказать "да присоединяйся к моей" (REQUIRED) или "нет, блядь, заведи свою, новую, отдельную" (REQUIRES_NEW).isolation— уровень изоляции, чтоб всякие грязные чтения и прочие фантомы не мозолили глаза.READ_COMMITTED, например.readOnly— скажи, что ты только читаешь, Спринг может какие-то оптимизации поделать, не дурак ведь.rollbackFor/noRollbackFor— тут ты сам решаешь, от каких исключений откатываться, а от каких — нет. Хозяин-барин, блядь.
Важный момент, ёпта! Чтобы эта магия вообще работала, не забудь в конфигурации включить управление транзакциями через @EnableTransactionManagement. А то будешь как дурак сидеть и думать, почему ничего не работает, в рот меня чих-пых!