Что такое @Transactional в Spring и как работает транзакционность?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
@Transactional в Spring: полное руководство
@Transactional — это аннотация Spring, которая управляет транзакциями на уровне методов и классов. Это один из основных инструментов для обеспечения консистентности данных в приложении.
Основные концепции
Транзакция — это набор операций, которые выполняются либо полностью, либо откатываются целиком (ACID принципы). @Transactional автоматически открывает транзакцию перед методом и коммитит её после успешного завершения.
Базовое использование
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public User createUser(String name) {
User user = new User(name);
return userRepository.save(user);
}
@Transactional(readOnly = true)
public User findUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
Как это работает под капотом
Spring использует AOP (Aspect-Oriented Programming) для создания прокси вокруг объектов. Перед методом открывается транзакция, после — коммитится или откатывается в зависимости от результата.
Уровни изоляции
- READ_UNCOMMITTED — грязное чтение, нарушает консистентность
- READ_COMMITTED — стандарт, не читаем незаяленные данные
- REPEATABLE_READ — фантомное чтение невозможно
- SERIALIZABLE — полная изоляция, может быть медленно
Распространение (propagation)
@Transactional(propagation = Propagation.REQUIRED)
// Использует существующую или создаёт новую (default)
@Transactional(propagation = Propagation.REQUIRES_NEW)
// Всегда создаёт новую
@Transactional(propagation = Propagation.NESTED)
// Создаёт savepoint
Обработка исключений
@Transactional(rollbackFor = Exception.class)
// Откатывается при ЛЮБОМ исключении
@Transactional(noRollbackFor = ValidationException.class)
// Не откатывается при ValidationException
Практический пример
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId).orElseThrow();
Account to = accountRepository.findById(toId).orElseThrow();
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
}
from.withdraw(amount);
to.deposit(amount);
}
Если исключение — откатываются обе операции.
Частые ошибки
- Вызов @Transactional из того же класса без прокси
- Незаявленные RuntimeException, которые не откатят транзакцию
- Дорогие операции внутри @Transactional
- Забыли указать rollbackFor для checked exceptions
Оптимизация
- Используй readOnly = true для методов без изменений
- Минимизируй время в транзакции
- Рассмотри Propagation.REQUIRES_NEW для независимых операций
- Избегай N+1 queries внутри транзакции
@Transactional — мощный инструмент, но требует понимания ACID принципов и особенностей реализации.