Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# @Transactional аннотация в Spring
@Transactional - одна из самых важных аннотаций в Spring для управления транзакциями. Это критично для обеспечения ACID свойств при работе с БД.
Как работает @Transactional
@Transactional создает прокси вокруг вашего метода, который автоматически управляет транзакциями.
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public User createUser(UserRequest request) {
User user = new User(request.getName(), request.getEmail());
return userRepository.save(user);
// Транзакция коммитится автоматически здесь
}
}
Spring оборачивает метод в try-catch, и если все успешно - коммитит, если ошибка - откатывает.
Аннотирование
На уровне класса
@Service
@Transactional
public class UserService {
// Все методы будут @Transactional
public void method1() { }
public void method2() { }
}
На уровне метода
@Service
public class UserService {
@Transactional
public void save(User user) { }
// Этот метод НЕ будет transactional
public void delete(User user) { }
}
Основные параметры
1. readOnly
// Для операций чтения
@Transactional(readOnly = true)
public List<User> getAllUsers() {
return userRepository.findAll();
}
// Для операций записи
@Transactional(readOnly = false) // или просто @Transactional
public User saveUser(User user) {
return userRepository.save(user);
}
readOnly = true оптимизирует транзакцию для чтения (БД может не открывать транзакцию).
2. propagation
Определяет, как транзакция распространяется на вложенные методы:
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void method1() {
method2(); // Использует ту же транзакцию
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method2() {
// Создает НОВУЮ транзакцию
// Даже если method1 откатится, method2 будет закоммичена
}
}
Типы propagation:
- REQUIRED (default): использует существующую или создает новую
- REQUIRES_NEW: всегда создает новую
- NESTED: создает savepoint
- SUPPORTS: использует если есть, иначе выполняет без транзакции
- MANDATORY: ошибка если нет транзакции
- NOT_SUPPORTED: выполняет БЕЗ транзакции
- NEVER: ошибка если есть транзакция
3. isolation
Уровни изоляции транзакций:
@Transactional(isolation = Isolation.READ_COMMITTED)
public User updateUser(User user) {
return userRepository.save(user);
}
Уровни:
- DEFAULT: используется default БД
- READ_UNCOMMITTED: грязные чтения
- READ_COMMITTED: только коммиченные данные
- REPEATABLE_READ: снимок данных в начале
- SERIALIZABLE: полная изоляция
4. timeout
@Transactional(timeout = 5) // 5 секунд
public void longOperation() {
// Если превысит 5 сек, откатится
expensiveOperation();
}
5. rollbackFor / noRollbackFor
// Откатит транзакцию при исключении
@Transactional(rollbackFor = CustomException.class)
public void process() {
throw new CustomException(); // откатит
}
// Не откатит при RuntimeException
@Transactional(noRollbackFor = IgnorableException.class)
public void flexibleProcess() {
throw new IgnorableException(); // коммитит несмотря на ошибку
}
По умолчанию откатывает только на RuntimeException и Error.
Практические примеры
Пример 1: Простая операция
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Transactional
public Order createOrder(OrderRequest request) {
Order order = new Order(request);
return orderRepository.save(order);
}
}
Пример 2: Несколько операций
@Service
public class PaymentService {
@Autowired
private AccountRepository accountRepository;
@Autowired
private TransactionRepository transactionRepository;
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId);
Account to = accountRepository.findById(toId);
from.debit(amount);
to.credit(amount);
accountRepository.save(from);
accountRepository.save(to);
Transaction transaction = new Transaction(fromId, toId, amount);
transactionRepository.save(transaction);
// Если любая операция fail, все откатится
}
}
Пример 3: Вложенные транзакции
@Service
public class ComplexService {
@Transactional
public void mainOperation() {
// основная логика
saveData();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveData() {
// Эта операция в отдельной транзакции
// Даже если mainOperation откатится, saveData остается
}
}
Важные моменты
1. Self-invocation проблема
@Service
public class UserService {
@Transactional
public void method1() {
method2(); // НЕ будет transactional!
}
@Transactional
public void method2() {
// Проблема: вызов через this игнорирует @Transactional
}
}
Решение: вызвать через bean:
@Service
public class UserService {
@Autowired
private UserService self;
public void method1() {
self.method2(); // Теперь будет transactional
}
}
2. Checked exceptions
По умолчанию не откатывает на checked exceptions:
@Transactional(rollbackFor = Exception.class)
public void process() throws Exception {
// Теперь откатит и на checked exceptions
}
3. Lazy loading проблема
@Transactional
public User getUser(Long id) {
return userRepository.findById(id); // lazy collections инициализируются
}
// БЕЗ @Transactional может быть LazyInitializationException
public void useUser() {
User user = getUser(1);
user.getOrders().size(); // Может быть ошибка
}
Лучшие практики
- Используй @Transactional только где нужно (не на контроллере)
- Размещай на сервисном слое
- Помни о performance при длительных операциях
- Используй readOnly = true для читающих операций
- Определяй timeout для длительных операций
- Избегай вложенных @Transactional методов без необходимости
- Помни о self-invocation проблеме
@Transactional - мощный инструмент, но нужно понимать, как он работает под капотом.