Spring Framework бере на себе всю мороку з транзакціями в додатку. Тобі достатньо позначити метод анотацією @Transactional, і Spring автоматично обгорне його виконання в транзакцію. Жодних явних begin, commit або rollback — фреймворк сам вирішує, коли почати транзакцію і як її завершити.
Види управління транзакціями
У Spring є два способи управління транзакціями:
- Декларативне управління транзакціями. Налаштовується анотаціями на кшталт
@Transactionalі використовується у 99% випадків (мінімум коду, максимум магії Spring). - Програмне управління транзакціями. Коли потрібен тонший контроль, можна використовувати спеціальні класи, такі як
TransactionTemplate.
Ми зосередимося на декларативному підході до управління транзакціями, хоча й торкнемося програмного. З декларативним підходом більшість задач вирішується однією анотацією — жодного ручного управління транзакціями, усе відбувається автоматично.
Архітектура управління транзакціями в Spring
За лаштунками транзакцій у Spring працює складний механізм. Як Spring знає, де почати транзакцію? Коли її завершити? І хто взагалі стежить за всім цим процесом? Давай розберемо, як усе влаштовано.
TransactionManager: головний диригент транзакцій
Spring використовує концепцію TransactionManager для управління транзакціями. Це центральний компонент, який координує початок, завершення і відкат транзакцій.
Ось основні реалізації, які найчастіше зустрічаються:
DataSourceTransactionManager— працює з JDBC (старий добрий SQL).JpaTransactionManager— для тих, хто використовує JPA/Hibernate. Ми будемо працювати з ним у багатьох прикладах.JtaTransactionManager— підходить для розподілених транзакцій через JTA (Java Transaction API).
Spring автоматично обирає підходящий TransactionManager, виходячи з твоїх залежностей. Тож, якщо ти використовуєш JPA, не дивуйся, коли побачиш, як Spring усе "вгадує". Друзі, це не вгадування — це Spring-автоконфігурація!
Підтримка різних технологій
Важливо зазначити, що Spring може керувати транзакціями на багатьох платформах і технологіях, включно з:
- JDBC — для роботи напряму з базами даних.
- JPA/Hibernate — для ORM-рішень.
- JTA — для роботи в розподілених системах (наприклад, мікросервісах).
Хочеш більше прикладів? Тоді давай створимо менеджера транзакцій у реальному додатку.
Приклад налаштування TransactionManager
Для роботи з транзакціями нам потрібно додати в наш додаток менеджер транзакцій. Налаштування в Spring Boot неймовірно просте. Розглянемо приклад для роботи з JPA:
@Configuration
@EnableTransactionManagement // Вмикаємо управління транзакціями
public class AppConfig {
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf); // Менеджер для JPA
}
}
Ось і все! Тепер Spring знає, який менеджер потрібно використовувати для управління транзакціями.
Трохи про декларативне і програмне управління
Давай швидко розберемо дві стратегії управління транзакціями, які пропонує Spring.
Декларативне управління транзакціями
У декларативному підході ти просто анотуєш метод або клас анотацією @Transactional. Spring бере на себе турботу про те, щоб почати транзакцію перед виконанням методу і завершити її після.
Приклад:
@Service
public class BankService {
@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
// Логіка переведення грошей
accountRepository.debit(fromAccountId, amount);
accountRepository.credit(toAccountId, amount);
}
}
Як бачиш, жодних ручних викликів begin() або commit() — тільки анотація і чистий бізнес-код.
Програмне управління транзакціями
Інколи треба керувати транзакціями вручну. Наприклад, якщо потрібне складне розгалуження логіки, де кожна гілка може працювати в своїй транзакції.
Приклад:
@Service
public class ManualTransactionService {
@Autowired
private PlatformTransactionManager transactionManager;
public void performAction() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(status -> {
// Твій код всередині транзакції
performSomeDBOperations();
return null; // null означає, що все пройшло успішно
});
}
}
Непогано, правда? Але давай будемо чесними: програмний підхід — це скоріше виняток, ніж правило.
Як працює транзакційне управління в Spring
Spring використовує AOP (аспектно-орієнтоване програмування) для управління транзакціями. Коли ти анотуєш метод @Transactional, Spring фактично створює проксі-об'єкт для твого біну. Цей проксі перехоплює виклики методів і обгортає їх у транзакції.
Простими словами:
- Перед викликом методу: Spring відкриває транзакцію.
- Виклик методу: виконується твій бізнес-код.
- Після виклику: транзакція або фіксується, або відкочується (якщо сталася помилка).
Приклад: налаштування транзакції і її використання
Давай розберемося з повною налаштуванням транзакцій у Spring Boot. Уявимо простий додаток для управління книжками в бібліотеці. Нам потрібно створити сутність Book, репозиторій, сервіс-слой і контролер.
Сутність Book
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
// Гетери і сетери...
}
Репозиторій
Ми будемо використовувати JpaRepository для роботи з базою даних.
public interface BookRepository extends JpaRepository<Book, Long> {
// Ніякого коду тут не потрібно, магія JpaRepository робить усе за нас
}
Сервіс-слой з транзакцією
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@Transactional
public Book saveBook(Book book) {
return bookRepository.save(book);
}
@Transactional
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
Тут ми говоримо Spring: "Ей, друже, обгорни всі методи в транзакцію!"
Контролер
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
@PostMapping
public ResponseEntity<Book> createBook(@RequestBody Book book) {
Book createdBook = bookService.saveBook(book);
return ResponseEntity.ok(createdBook);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
return ResponseEntity.noContent().build();
}
}
Тепер ти можеш створювати й видаляти книжки у своїй бібліотеці, і все це під транзакціями.
Важливі нюанси: про помилки та несподівані проблеми
Однією з частих помилок є анотування приватних методів анотацією @Transactional. Пам'ятай, транзакції працюють через проксі, а отже, Spring бачить лише виклики публічних методів.
Також важливо пам'ятати, що якщо метод викликає інший метод того ж класу, анотація @Transactional на другому методі може бути проігнорована.
Ти бачиш, наскільки просто й елегантно Spring справляється з транзакціями, надаючи нам потужний інструмент для управління даними та узгодженістю. У наступних лекціях ми заглибимося в анотацію @Transactional і її налаштування. Готуйся до нових відкриттів!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ