← Назад к вопросам

Что случится с методом если не обернуть его аннотацией Transactional?

1.8 Middle🔥 171 комментариев
#Spring Framework#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что случится с методом если не обернуть его аннотацией @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 открытой, позволяет загружать связанные данные и гарантирует атомарность операций с БД.

Что случится с методом если не обернуть его аннотацией Transactional? | PrepBro