Как вызвать транзакционный метод из того же класса?

Посмотреть в Telegram: @JavaSobes/292
В Spring Framework существует аннотация @Transactional. Ей помечается метод или класс, весь код которого должен выполняться в рамках транзакции. Обычно имеется в виду транзакция базы данных, но вообще это понятие определяется используемым transactionManager-ом. Настройки, такие как уровень изоляции, стратегия роллбэка и прочие, определяются через параметры этой аннотации.

В теории, @Transactional делает метод транзакционным для этого класса и всех его наследников. На практике же, по умолчанию, если вызвать транзакционный метод Foo.bar() из Foo.baz(), то транзакция не создастся.

Это происходит вследствие того, что по умолчанию Spring AOP добавляет код открытия/закрытия транзакции через динамический proxy класс. То есть, вместо Foo инджектится нечто, похожее на код на изображении.

Первый вариант решения проблемы – вместо аннотации использовать TransactionTemplate, то есть обернуть код в транзакцию вручную. Примеры использования можно посмотреть в этой статье.

Другой, более универсальный, но более сложный в конфигурации способ – переключить режим работы Spring AOP с динамических прокси на нечто другое. Обычно применяется библиотека AspectJ:

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)


В Spring AOP есть понятие weaving – этап добавления дополнительной функциональности (аспектов). В нашем случае, это код открытия/закрытия транзакции. Чтобы заработал weaving AspectJ этапа компиляции, в сборку нужно добавить плагин: aspectj-maven-plugin для maven, gradle-aspectj для gradle.

Подробнее об экспериментах с разными режимами Spring AOP можно почитать в статье на хабре.