← Назад к вопросам
Что происходит при возникновении исключения в транзакции
1.7 Middle🔥 131 комментариев
#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что происходит при возникновении исключения в транзакции
При возникновении исключения в транзакции происходит откат всех выполненных операций - это обеспечивает целостность данных.
Поведение по умолчанию
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId).get();
Account to = accountRepository.findById(toId).get();
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
// Если тут выброситься исключение - откатит ВСЮ транзакцию
if (to.getBalance().compareTo(BigDecimal.ZERO) < 0) {
throw new InsufficientFundsException();
}
}
Если выброситься любое RuntimeException:
- Все изменения откатываются (ROLLBACK)
- Ни один UPDATE не будет применён
- Состояние БД как будто операция не начиналась
Checked vs Unchecked исключения
@Transactional
public void process() throws IOException {
// Checked exception - НЕ откатывает по умолчанию
throw new IOException("Ошибка файла");
}
@Transactional
public void process() {
// Unchecked exception - откатывает всегда
throw new RuntimeException("Ошибка");
}
Это поведение можно переопределить:
@Transactional(rollbackFor = {IOException.class, MyException.class})
public void process() throws IOException {
// Теперь IOException тоже откатит транзакцию
throw new IOException();
}
@Transactional(noRollbackFor = {RuntimeException.class})
public void process() {
// RuntimeException НЕ откатит транзакцию
throw new RuntimeException();
}
Откат с try-catch
@Transactional
public void process() {
try {
// Операции с БД
userRepository.save(user);
} catch (Exception e) {
// Если ловишь исключение - откат НЕ происходит!
System.out.println("Ошибка обработана");
}
// Код продолжает выполняться, транзакция коммитится
}
Решение - пробросить исключение дальше или явно откати:
@Transactional
public void process() {
try {
userRepository.save(user);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// или просто пробросить:
throw new RuntimeException("Ошибка обработки", e);
}
}
Частичный откат с вложенными транзакциями
@Transactional
public void parentTransaction() {
userRepository.save(user1);
try {
childTransaction();
} catch (Exception e) {
// Вложенная транзакция откатилась, но родительская продолжает
System.out.println("Обработана ошибка");
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void childTransaction() {
userRepository.save(user2);
throw new RuntimeException(); // Откатит только user2
}
// Результат: user1 сохранён, user2 нет
Точка сохранения (Savepoint)
@Transactional
public void complexOperation() {
userRepository.save(user1);
// Создаём точку сохранения
Object savepoint = TransactionAspectSupport
.currentTransactionStatus()
.createSavepoint();
try {
userRepository.save(user2);
// Некорректная операция
Integer.parseInt("abc");
} catch (Exception e) {
// Откатываем только до точки сохранения
TransactionAspectSupport
.currentTransactionStatus()
.rollbackToSavepoint(savepoint);
}
}
Правила и best practices
- Используй unchecked exceptions для отката автоматический
- Не ловй исключения в @Transactional методах без пробрасывания
- Явно отката через setRollbackOnly() если перехватываешь
- Используй REQUIRES_NEW для независимых вложенных операций
- Логируй перед откатом для отладки
Понимание механизма откатов критично для построения надёжных систем работы с БД.