Исключение распространяется после того, как уже поймали - PullRequest
6 голосов
/ 25 октября 2011

У меня происходит самое странное, и я не могу понять, почему. Лучший способ описать это - предоставить упрощенный пример:

@Service
@Transactional
public class Foo{
    public ModelAndView delete(@ModelAttribute("abc") Long id) {
        ModelAndView mav = new ModelAndView();
        try {
            getDaoService().delete(id); //Calls Bar.delete()
        } catch (final Exception e) {
            // Add a custom error message to the mav for the user to see
            mav.getModelMap().addAttribute(blah, blah);
        }
        return mav;
    }
}

@Service
@Transactional
public class Bar {
    public void delete(final E entity) throws HibernateException {
        if (null != entity) {
            try {
                sessionFactory.getCurrentSession().delete(entity);
            } finally {
                sessionFactory.getCurrentSession().flush();
            }
        }
    }
}

В данном конкретном случае я пытаюсь удалить объект с нарушением ограничения (ORA-02292). Я ожидаю, что удаление не удастся из-за этого. Когда удаление не удается, я хочу показать пользователю соответствующее пользовательское сообщение.

Вместо того, чтобы показывать пользователю пользовательское сообщение, вызов не выполняется и на экране отображается следующее:

org.springframework.transaction.UnexpectedRollbackException: транзакция откат, потому что он был помечен как только для отката

Когда я использую отладчик, я вижу, что ошибка правильно перехвачена и что у объекта ModelAndView есть собственное сообщение внутри него. Итак, я понятия не имею, почему исключение все еще выдается после того, как оно было поймано и обработано. Кто-нибудь знает, почему это происходит?

Ответы [ 4 ]

5 голосов
/ 25 октября 2011

Проблема заключается в том, что, как только выдается исключение, Spring внутренне помечает tx как только для отката.Это полностью отделено от обработки исключений Java.У вас есть несколько вариантов:

  • Убедитесь, что ожидаемое исключение не вызывает исключений, которые расширяются RuntimeException;Пружина откатывается назад, когда она имеет тип RuntimeException ( см. Эту страницу , раздел 10.5.3). HibernateException расширяет RuntimeException , поэтому вы получаете маркер отката.
  • Запустите каждый tx в своей собственной транзакции, переместив метод транзакции в свой собственный класс и аннотируя его с помощью @Transactional(propagation=Propagation.REQUIRES_NEW),Тогда каждый вызов будет выполняться в своем собственном времени и не повлияет на общее время передачи.
  • Используйте упомянутую венушку в стиле noRollbackForClassName.Но используйте с осторожностью, по упомянутой причине.
5 голосов
/ 25 октября 2011

В аннотации @Transactional вы можете указать, следует ли откатывать транзакцию из-за данного исключения, используя атрибут noRollbackForClassName.Вы можете сделать это аналогично этому.

@Service
@Transactional(noRollbackForClassName = "java.lang.Exception")
public class YourClass {
    ...
}

Однако, обратите внимание, что просто сказать noRollbackForClassName = "java.lang.Exception" будет означать, что он не откатится ни для какого исключения (или его подклассов), следовательно, это не очень хорошая практика.

Что вы должны сделать, это выяснить, какое исключение на самом деле выдается первым (может быть, распечатав e.getClass().getName()), а затем установить имя этого класса в качестве значения noRollbackForClassName.

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

1 голос
/ 25 октября 2011

Исключение выдается при удалении Bar # и перехватывается при удалении Foo #.Существует аннотация @Transactional на Bar # delete, которая пересекается до того, как исключение будет перехвачено.Эта внутренняя транзакция участвует во внешней транзакции, поэтому вся транзакция помечена для отката.

Чтобы избежать этого, вы можете удалить аннотацию @Transactional для Bar # delete.Этот метод уже вызывается в рамках другой транзакции.

0 голосов
/ 02 апреля 2013

Добавьте свойство "globalRollbackOnParticipationFailure" к определению bean-компонента hibernateTransactionManager следующим образом.

<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="hibernateSessionFactory" />
    **<property name="globalRollbackOnParticipationFailure" value="false" />**
</bean>
...