Програмне управління транзакціями зазвичай може бути чудовою ідеєю лише якщо у тебе є невелика кількість транзакційних операцій. Наприклад, якщо у тебе є вебдодаток, якому потрібні транзакції тільки для певних операцій оновлення, можна не налаштовувати транзакційні проксі за допомогою Spring або будь-якої іншої технології. У цьому випадку підхід з використанням TransactionTemplate може бути доречним. Можливість явного зазначення імені транзакції доступна лише за умови використання програмного підходу до управління транзакціями.

З іншого боку, якщо програма має безліч транзакційних операцій, декларативне управління транзакціями зазвичай має сенс. Воно не включає управління транзакціями до бізнес-логіки і його нескладно конфігурувати. При використанні Spring Framework, а не CMT із EJB, вартість конфігурації декларативного управління транзакціями значно знижується.

Події в межах транзакції

Починаючи з версії Spring 4.2, слухач події може бути прив'язаний до фази транзакції. Типовий приклад — обробка події за успішного завершення транзакції. Це дозволяє використовувати події гнучкіше, якщо результат поточної транзакції дійсно важливий для слухача.

Ти можеш зареєструвати звичайного слухача події за допомогою анотації @EventListener. Якщо потрібно прив'язати його до транзакції, використовуй анотацію @TransactionalEventListener. Після цього за замовчуванням слухач прив'яжеться до фази фіксації транзакції.

У такому прикладі демонструється ця концепція. Припустимо, що компонент публікує подію, створену в певному порядку, а нам потрібно визначити слухача, який повинен обробляти цю подію лише після успішної фіксації транзакції, в якій вона була опублікована. Наступний приклад встановлює такий слухач:

Java
@Component
public class MyComponent {
    @TransactionalEventListener
    public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
        // ...
    }
}
Kotlin
@Component
class MyComponent {
    @TransactionalEventListener
    fun handleOrderCreatedEvent(creationEvent: CreationEvent<Order>) {
        // ...
    }
}

Аннотація @TransactionalEventListener відкриває атрибут phase, який дозволяє налаштувати фазу транзакції, до якої має бути прив'язаний слухач. Справжніми фазами є BEFORE_COMMIT, AFTER_COMMIT (за замовчуванням), AFTER_ROLLBACK, а також AFTER_COMPLETION, яка поєднує завершення транзакції (чи це фіксація або відкат).

Якщо транзакція не виконується, слухач взагалі не викликається, оскільки ми не можемо дотримати необхідну семантику. Однак можна перевизначити цю логіку роботи, встановивши атрибут fallbackExecution анотації в true.

Анотація @TransactionalEventListener працює тільки з транзакціями, що управляються за допомогою PlatformTransactionManager, прив'язаними до потоку. Реактивна транзакція, якою управляє ReactiveTransactionManager, використовує контекст Reactor замість локальних атрибутів потоку, тому з точки зору слухача подій сумісної активної транзакції, в якій він може брати участь, не існує.

Інтеграція з сервером програм

Абстракція транзакцій Spring загалом залежить від сервера додатків. Крім того, клас JtaTransactionManager зі Spring (який може опціонально виконувати пошук об'єктів UserTransaction та TransactionManager з JTA через JNDI) автоматично визначає місце розташування останнього об'єкта, яке залежить від сервера програм. Наявність доступу до TransactionManager з JTA дозволяє розширити семантику транзакцій – зокрема, підтримувати припинення транзакцій. Подробиці див. у javadoc JtaTransactionManager.

JtaTransactionManager зі Spring є типовим способом виконання на серверах додатків Java EE і, як відомо, працює на всіх поширених серверах. Розширені функціональні можливості, такі як припинення транзакцій, також працюють на багатьох серверах (включно з GlassFish, JBoss і Geronimo) без необхідності створення особливої конфігурації. Однак для повноцінної підтримки припинення транзакцій та подальшої розширеної інтеграції Spring містить спеціальні адаптери для WebLogic Server та WebSphere. Ці адаптери описані в наступних розділах.

Для стандартних сценаріїв, включно з WebLogic Server та WebSphere, використовуй допоміжний елемент конфігурації <tx:jta-transaction-manager/>. При конфігуруванні цей елемент автоматично визначає основний сервер та обирає найкращий диспетчер транзакцій, доступний для платформи. Це означає, що не потрібно явно конфігурувати специфічні для сервера класи адаптерів (як це описано в наступних розділах). Швидше вони вибираються автоматично, водночас стандартний JtaTransactionManager є поворотним варіантом за замовчуванням.

WebSphere від IBM

У WebSphere 6.1.0.9 і вище рекомендованим диспетчером транзакцій JTA до Spring є WebSphereUowTransactionManager. Цей спеціальний адаптер використовує API-інтерфейс UOWManager від IBM, який доступний у WebSphere Application Server 6.1.0.9 та пізніших версіях. Завдяки цьому адаптеру припинення транзакцій, що управляються Spring (призупинення та відновлення PROPAGATION_REQUIRES_NEW), офіційно підтримується IBM.

WebLogic Server від Oracle

На WebLogic Server 9.0 або вище зазвичай використовується WebLogicJtaTransactionManager замість стандартного класу JtaTransactionManager. Цей спеціальний для WebLogic підклас звичайного JtaTransactionManager підтримує весь функціонал визначень транзакцій Spring в оточенні транзакцій, керованих WebLogic, крім стандартної семантики JTA. Функції включають імена транзакцій, рівні ізоляції для кожної транзакції та належне відновлення транзакцій у будь-яких випадках.

Вирішення поширених проблем

У цьому розділі описано розв'язання деяких поширених проблем.

Використання неправильного диспетчера транзакцій для певного DataSource

Використовуй правильну реалізацію PlatformTransactionManager на основі обраних тобою транзакційних технологій та вимог. У разі правильного використання Spring Framework надасть найпростішу абстракцію, що портується. Якщо використовуються глобальні транзакції, потрібно користуватися класом org.springframework.transaction.jta.Jta.JtaTransactionManager (або його підкласом, специфічним для сервера додатків) для всіх операцій транзакції. В іншому випадку інфраструктура транзакцій намагатиметься виконувати локальні транзакції на таких ресурсах, як контейнерні екземпляри DataSource. Такі локальні транзакції не мають сенсу, а сервер додатків, що справно працює, вважає їх помилками.

Додаткові джерела

Детальнішу інформацію про підтримку транзакцій у Spring Framework див:

  • Розподілені транзакції в Spring, з і без XA – це презентація JavaWorld, в якій Девід Сайєр із Spring познайомить тебе із сімома шаблонами для розподілених транзакцій у додатках Spring, три з них з XA та чотири без.

  • Стратегії проєктування транзакцій Java – це книга, доступна в InfoQ, яка є добре обґрунтованим вступом до транзакцій у Java. До того ж він містить паралельні приклади налаштування та використання транзакцій як у Spring Framework, так і в EJB3.