Программное управление транзакциями обычно может быть отличной идеей, только если у вас есть небольшое количество транзакционных операций. Например, если у вас есть веб-приложение, которому требуются транзакции только для определенных операций обновления, можно не настраивать транзакционные прокси с помощью 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.