Что случится с методом если не обернуть его аннотацией Transactional?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что случится с методом если не обернуть его аннотацией @Transactional?
Аннотация @Transactional в Spring/Hibernate управляет границами транзакций. Если не обернуть метод этой аннотацией, то возникнет проблема лениво загруженных отношений (Lazy Loading) и потеря транзакционного контекста.
Проблема без @Transactional
1. LazyInitializationException Если в классе сущности есть отношение с FetchType.LAZY (по умолчанию для @OneToMany), то попытка получить это отношение после закрытия сессии Hibernate приведёт к ошибке:
@Entity
public class User {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY) // Ленивая загрузка
private List<Order> orders;
}
// Без @Transactional
public void getUser(Long id) { // ❌ Без @Transactional
User user = userRepository.findById(id).orElse(null);
// На этом моменте сессия уже закрыта
user.getOrders().size(); // LazyInitializationException!
// org.hibernate.LazyInitializationException: could not initialize proxy - no Session
}
// С @Transactional
@Transactional
public void getUser(Long id) { // ✅ С @Transactional
User user = userRepository.findById(id).orElse(null);
user.getOrders().size(); // OK, сессия ещё открыта
}
Что делает @Transactional
1. Открывает транзакцию в начале метода
@Transactional
public void updateUser(User user) {
// Spring открыл транзакцию
userRepository.save(user);
// Все ленивые отношения доступны
// Сессия Hibernate активна
} // Spring коммитит транзакцию автоматически
2. Автоматический коммит/откат
- Если метод завершился успешно → транзакция коммитится
- Если выброшено исключение → откат (rollback)
@Transactional
public void transferMoney(Account from, Account to, BigDecimal amount) {
from.withdraw(amount);
to.deposit(amount);
accountRepository.save(from);
accountRepository.save(to);
// Если здесь будет исключение → откат обоих операций
}
3. Обработка исключений
@Transactional(rollbackFor = PaymentException.class)
public void processPayment(Order order) throws PaymentException {
// Если выбросится PaymentException → откат
paymentService.charge(order.getTotal());
order.setStatus("PAID");
orderRepository.save(order);
}
Проблемы без @Transactional
1. LazyInitializationException
// ❌ Без @Transactional
public User getUserWithOrders(Long id) {
return userRepository.findById(id).orElse(null);
}
// В контроллере
@GetMapping("/users/{id}")
public UserDTO getUser(@PathVariable Long id) {
User user = userService.getUserWithOrders(id);
return userDTO.from(user); // LazyInitializationException!
}
2. Грязные данные (Dirty Checking не работает) Без транзакции изменения могут не сохраниться:
// ❌ Без @Transactional
public void updateUserName(Long id, String newName) {
User user = userRepository.findById(id).orElse(null);
user.setName(newName);
// Сессия закрыта, изменение может не сохраниться!
}
// ✅ С @Transactional
@Transactional
public void updateUserName(Long id, String newName) {
User user = userRepository.findById(id).orElse(null);
user.setName(newName);
// Hibernate отследит изменение и сохранит при коммите
}
Где нужна @Transactional
- Методы сервисов, которые работают с БД
- Методы, которые используют ленивые отношения (@OneToMany, @ManyToMany)
- Сложные операции, которые должны быть атомарными
- Методы, в которых нужна откатка при ошибке
Альтернативы
1. Использовать FetchType.EAGER (не рекомендуется для @OneToMany)
@OneToMany(fetch = FetchType.EAGER) // Всегда загружать
private List<Order> orders;
2. Явно инициализировать в одной транзакции
@Transactional(readOnly = true)
public User getUserWithOrders(Long id) {
User user = userRepository.findById(id).orElse(null);
Hibernate.initialize(user.getOrders()); // Явная инициализация
return user;
}
Итог
Без @Transactional вы получите LazyInitializationException при попытке доступа к ленивым отношениям. @Transactional держит сессию Hibernate открытой, позволяет загружать связанные данные и гарантирует атомарность операций с БД.