UnexpectedRollbackException - полный анализ сценариев

Все, что я знаю об этом исключении, - это Spring документация и некоторые сообщения на форуме с замороженными разработчиками, вставляющими огромные трассировки стека, и ответов нет.

Из Spring документации:

Брошенный, когда попытка совершить транзакцию привела к неожиданному откату

Я хочу разобраться раз и навсегда

  • Что именно это вызывает?

    • Где произошел откат? в коде сервера приложений или в базе данных?
    • Было ли это вызвано конкретным базовым исключением (например, что-то из java.sql. *)?
    • Связано ли это с Hibernate? Связано ли это с Spring Transaction Manager (без JTA в моем случае)?
  • Как избежать этого? есть ли какая-либо лучшая практика, чтобы избежать этого?

  • Как отлаживать его? кажется, трудно воспроизвести, какие-либо проверенные способы его устранения?

Ответы

Ответ 1

Прокрутите еще немного в журнале (или увеличьте его размер буфера), и вы увидите, что именно вызвало исключение.

Если это не так, проверьте методы getMostSpecificCause() и getRootCause() UnexpectedRollbackException - они могут быть полезны.

Ответ 2

Я нашел, что это ответ на оставшийся вопрос: https://jira.springsource.org/browse/SPR-3452

Думаю, нам нужно различать между "логическими" областями транзакций и "физические" транзакции здесь...

Что создает PROPAGATION_REQUIRED, является логическая область транзакций для каждого метод, к которому он применяется. каждый такая область логической транзакции может индивидуально решать только откаты статус, с внешней транзакцией объем логически независим от внутренний объем транзакции. из конечно, в случае стандартного PROPAGATION_REQUIRED поведение, они будут сопоставлены с одним и тем же физическим сделка. Таким образом маркер только для отката установить во внутренней области транзакции влияет на внешнюю транзакцию шанс на самом деле совершить. Однако, поскольку внешний объем транзакций не принимать решения о самом откате, отката (тихо запускается внутренний объем транзакции) неожиданным на этом уровне - почему UnexpectedRollbackException бросается.

PROPAGATION_REQUIRES_NEW, напротив, использует полностью независимую транзакция для каждого затронутого объем транзакции. В этом случае основные физические транзакции быть разными и, следовательно, может совершать или отката независимо, с внешним транзакция не зависит от внутренней статус отката транзакции.

PROPAGATION_NESTED снова отличается в том, что он использует один физический транзакция с несколькими точками сохранения что он может вернуться назад. Такие частичные откаты разрешают внутреннюю транзакцию для запуска отката для его с внешней транзакцией способность продолжать физическое транзакции, несмотря на некоторые операции отбросив назад. Это обычно отображается на точки сохранения JDBC, поэтому будет работать только с ресурсом JDBC транзакции (Spring 's DataSourceTransactionManager).

Чтобы завершить обсуждение: UnexpectedRollbackException также может быть брошенным без приложения когда-либо установив маркер только для отката сам. Вместо этого транзакция инфраструктура может решить, что единственным возможным результатом является отката, из-за ограничений в текущее состояние транзакции. Это особенно применительно к XA сделки.

Как я сказал выше, бросая исключение во внутренней транзакции области охвата, а затем поймать это исключение в внешний охват и перевод его в бесшумный вызов setRollbackOnly там должен работать ваш сценарий. вызов внешней транзакции будет никогда не см. исключение. Поскольку вы беспокоиться только о таких тихих откатах из-за особых требований навязанный вызывающим, я бы даже утверждают, что правильная архитектура решение заключается в использовании исключений внутри уровень обслуживания и перевод эти исключения в молчаливые откаты на уровне фасада сервиса (справа прежде чем вернуться к этому специальному вызывающий абонент).

Так как ваша проблема, возможно, не только об исключениях отката, но скорее о любых исключениях, брошенных с вашего уровня обслуживания, вы можете даже использовать стандартные исключения откат на всем протяжении сервисный уровень, а затем ловить и записывать такие исключения после совершения сделки уже завершено, в некоторых адаптация фасада переводит ваш уровень обслуживания исключения в UI-специфическую ошибку состояния.

Юрген

Ответ 3

Хорошо, я могу рассказать вам, как воспроизвести UnexpectedRollbackException. Я работал над своим проектом, и я получил это UnexpectedRollbackException в следующей ситуации. В моем проекте есть уровни управления, сервиса и дао. То, что я сделал, находится в моем классе уровня обслуживания,

class SomeServiceClass {
    void outerTransaction() {
        // some lines of code
        innerTransaction();
        //AbstractPlatformTransactionManager tries to commit but UnexpectedRollbackException is thrown, not here but in controller layer class that uses SomeServiceClass.
    }

    void innerTransaction() {
        try {
            // someDaoMethod or someDaoOperation that throws exception
        } catch(Exception e) {
            // when exception is caught, transaction is rolled back, outer transaction does not know about it.
            // I got this point where inner transaction gets rolled back when I set HibernateTransactionManager.setFailEarlyOnGlobalRollbackOnly(true)
            // FYI : use of following second dao access is wrong, 
            try {
                // again some dao operation
            } catch(Exception e1) {
                throw e2;
            }
        }
    }
}