Косвенный вызов метода Hibernate / JPA теряет транзакцию - PullRequest
3 голосов
/ 28 июня 2011

Я использую Spring / JPA2 / hibernate с этим кодом:

class A {
  @Autowired
  B b;

  @RequestMapping("/test")
  public void test(final HttpServletRequest r1, HttpServletResponse r2) throws ... {

    b.inner();   // Works

    b.outer();   // javax.persistence.TransactionRequiredException: 
                 // no transaction is in progress ... :|
}

@Component
class B {
   @PersistenceContext
   EntityManager em;

   public void outer() { inner(); }

   @Transactional
   public void inner() { em.flush(); }
}

Почему inner() только при косвенном вызове теряет транзакцию?

Ответы [ 2 ]

5 голосов
/ 28 июня 2011

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;
0 голосов
/ 28 июня 2011

Транзакционный контекст длится на протяжении всей жизни вашего боба.Нотация @Transactional имеет область действия всего компонента, и вы должны аннотировать свой @Component как @Transactional, например

@Transactional
@Component
class B {
    @PersistenceContext 
    EntityManager em;

    public inner() { }
    public outer() { }
}

Методы внутренние и внешние должны выполнять отдельные единицы работы.Если вам нужна какая-то вспомогательная функция или что у вас хорошо, но единица работы, которая требует транзакционной границы, должна быть ограничена для каждого метода.См. Весенние документы на @Transactional http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...