http://static.springsource.org/spring/docs/current/reference/transaction.html#transaction-declarative-annotations
В режиме прокси (который используется по умолчанию) перехватываются только внешние вызовы методов, поступающие через прокси. Это означает, что самовывоз, по сути, метод в целевом объекте, вызывающий другой метод целевого объекта, не приведет к реальной транзакции во время выполнения, даже если вызванный метод помечен @ Transactional.
Подумайте об использовании режима AspectJ (см. Атрибут mode в таблице ниже), если вы ожидаете, что при выполнении собственных вызовов также будут выполняться транзакции. В этом случае, во-первых, не будет прокси; вместо этого целевой класс будет соткан (то есть его байт-код будет изменен), чтобы превратить @Transactional в поведение во время выполнения для любого метода.
Ссылка @Autowired
B b
(внутри класса A
) заключена в прокси-сервер, поддерживающий транзакции Spring AOP.
Когда вызывается b.inner()
, вы вызываете его для экземпляра с поддержкой транзакций, и он помечается как @Transactional
. Таким образом, запускается управляемая Spring транзакция.
Когда вызывается b.outer()
, он также относится к экземпляру с поддержкой транзакций, но это , а не @Transactional
. Таким образом, управляемая Spring транзакция не запущена.
Как только вы находитесь внутри вызова outer()
, вызов inner()
- это , а не , проходящий через прокси с поддержкой транзакций, он вызывается напрямую. Это так же, как this.inner()
. Поскольку вы вызываете его напрямую, а не через прокси-сервер, он не имеет семантики, учитывающей транзакции Spring.
Поскольку транзакция не была начата, это приводит к TransactionRequiredException
.
Возможные решения включают создание метода outer()
@Transactional
.
@Transactional
public void outer() { inner(); }
Или создание всего класса @Transactional
.
@Transactional
@Component
class B {
@PersistenceContext
EntityManager em;