Вложенная транзакция и setRollbackOnly() EJBContext

Я читаю Управление транзакциями Java EE 7, и меня смущает концепция вложенной транзакции и функциональность EJBContext#setRollbackOnly().

Скажем, у меня есть два сеансовых компонента, Bean1Impl и Bean2Impl, и их подписи:

@Stateless 
@TransactionManagement(TransactionManagementType.CONTAINER)
public class Bean1Impl implements Bean1 {

    @Resource 
    private EJBContext context;

    @TransactionAttribute(REQUIRED)
    public void method1() {
        try {
            //some operations such as persist(), merge() or remove().
        }catch(Throwable th){
            context.setRollbackOnly();
        }
    }
}

@Stateless 
@TransactionManagement(TransactionManagementType.CONTAINER)
public class Bean2Impl implements Bean2 {

    @Resource 
    private EJBContext context;

    @TransactionAttribute(REQUIRED)
    public void method2() {
        try {
            //some operations such as persist(), merge() or remove().
            //an exception has been thrown
        }catch(Throwable th){
            context.setRollbackOnly();
        }
    }
}

Как указано в учебнике по Java EE 7:

51.3.1.1 Обязательный атрибут

Если клиент выполняется внутри транзакции и вызывает метод корпоративного компонента, этот метод выполняется внутри транзакции клиента. Если клиент не связан с транзакцией, контейнер запускает новую транзакцию перед запуском метода.

Атрибут Required — это неявный атрибут транзакции для всех методов корпоративного компонента, работающих с разграничением транзакций, управляемых контейнером. Обычно вы не устанавливаете обязательный атрибут, если вам не нужно переопределить другой атрибут транзакции. Поскольку атрибуты транзакций являются декларативными, их можно легко изменить позже.

В этом случае мне не нужно указывать объявление аннотации @TransactionAttribute(REQUIRED) в методах Bean1Impl#method1() и Bean2Impl#method2(). Я прав?

Таким образом, в приведенном выше коде транзакция Bean2Impl#method2() будет выполняться внутри транзакции Bean1Impl#method1().

Могу ли я считать это вложенной транзакцией?

Если внутри метода Bean2Impl#method2() было выброшено Exception, что в конечном итоге привело бы к вызову метода EJBContext.setRollbackOnly() из блока catch и, как и ожидалось, он должен откатить операции, выполненные в блоке try этого метода. Что в этом случае произойдет с транзакцией, а также с Bean1Impl#method1(). Его тоже откатят? Я имею в виду:

Что произойдет, если будет звонок EJBContext.setRollbackOnly() от Bean2Impl#method2() и

  • Bean2Impl#method2() вызывается из метода Bean1Impl#method1() перед любой операцией с базой данных, такой как сохранение, слияние или удаление.
  • Bean2Impl#method2() вызывается из метода Bean1Impl#method1() после любой операции с базой данных, такой как сохранение, слияние или удаление.

И, наконец, что произойдет, если метод Bean2Impl#method2() будет выполнен успешно, но EJBContext.setRollbackOnly() будет вызван из Bean1Impl#method1() после успешного возврата Bean2Impl#method2()?


person Tapas Bose    schedule 29.12.2013    source источник
comment
Скорее всего, это распределенная транзакция, а не вложенная.   -  person Sami Korhonen    schedule 29.12.2013


Ответы (2)


Чтобы добавить к правильному ответу @Philippe Marshall и вашему комментарию - REQUIRES_NEW создаст новую транзакцию, независимую от первой. Они не вложенные. Первая транзакция приостановлена, пока вторая активна. Как только вторая транзакция фиксируется, первая возобновляется.

Вам не нужно setRollbackOnly() вручную. Большинство PersistenceException сделают это при необходимости. Вы ничего не получите, откатывая транзакции, которые вам не нужны. Например, при запросе данных вы можете получить NoResultException или NonUniqueResultException. Они не приводят к откату транзакции, поскольку нет риска несоответствия между контекстом персистентности и БД.

Вам не нужно указывать ни @TransactionAttribute(REQUIRED), ни @TransactionManagement(TransactionManagementType.CONTAINER) - оба являются настройками по умолчанию.

РЕДАКТИРОВАТЬ: чтобы ответить на ваши дополнительные вопросы:

Я предполагаю @TransactionAttribute(REQUIRES_NEW) на method2 и, следовательно, две отдельные транзакции.

Если есть Exception, который приводит к откату транзакции в method2, транзакция из method1 не будет откатана, если Исключение будет перехвачено. Если Exception не пойман, обе транзакции будут отброшены.

При установке флага отката на транзакции не имеет значения, происходит ли это до или после операций БД, так как откатывается вся транзакция.

Как только method2 возвращается, его транзакция фиксируется. Последующий откат или подтверждение транзакции с method1 не влияет на результаты первой транзакции.

Общий совет - не перехватывайте Throwable - это слишком широко, и вы можете проглотить исключения, которые вы бы предпочли распространить на поверхность.

person kostja    schedule 29.12.2013

Это не вложенные транзакции, JavaEE/JTA не поддерживает вложенные транзакции. Если #method2() вызывается из #method1(), он выполняется в той же транзакции. Если вы хотите провести другую транзакцию, вам нужно #REQUIRES_NEW. EJBContext.setRollbackOnly() работает только с текущей транзакцией. Обратите внимание, что существует вероятность того, что после вызова EJBContext.setRollbackOnly() все операции с транзакционным ресурсом, включая чтение, вызовут исключение (JBoss AS 5.1 сделал это, текущее поведение неизвестно).

Обновление:

    }catch(Throwable th){
        context.setRollbackOnly();
    }

Вам не нужно это для исключений во время выполнения, это поведение EJB по умолчанию.

person Philippe Marschall    schedule 29.12.2013
comment
Из вашего ответа я понимаю, что вызов REQUIRES_NEW создаст вложенную транзакцию. И мне не нужен какой-либо механизм обработки исключений для обработки RuntimeException. - person Tapas Bose; 29.12.2013
comment
REQUIRES_NEW создает независимую транзакцию - person Philippe Marschall; 30.12.2013