Что такое правильный шаблон кода для завершения транзакций в Java (откат при исключении и фиксация при успехе)? - PullRequest
7 голосов
/ 13 января 2010

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

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

protected void doIt() {
  // for JDBC connection transaction may be started automatically
  // but assume we start it here
  Tran tran = session.beginTran();
  try {
    // here comes code that does some processing
    // modifies some data in transaction
    // etc.

    // success - so commit
    tran.commit();

  } catch (Exception ex) { // many different exceptions may be thrown
                           // subclass of RuntimeException, SQLException etc.
     // error - so rollback
     tran.rollback();

     // now rethrow ex
     throw ex;              // this line causes trouble, see description below
  }      
}

Теперь - ошибка метода компиляции doIt. Он должен объявлять throws Exception, но это неприемлемо, поскольку метод doIt используется во многих местах, а добавление throws Exception приводит к последующим модификациям в местах прямого и косвенного использования doIt. Это связано с известной проблемой проектирования языка Java с объявленными исключениями.

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

Я знаю, что мог бы сделать что-то вроде throw new RuntimeException(ex), но это исключение другого класса, и я хочу избежать такого решения.

Ответы [ 3 ]

8 голосов
/ 13 января 2010

Я бы пошел на что-то вроде этого.

protected void doIt() {
  // for JDBC connection transaction may be started automatically
  // but assume we start it here
  Tran tran = session.beginTran();
  bool success = false;
  try {
    // here comes code that does some processing
    // modifies some data in transaction
    // etc.

    // success - so commit
    tran.commit();
    success = true;
  } finally { 
     // error - so rollback
     if (! success)
       tran.rollback();
  }      
}

.. или если у trans есть метод, с помощью которого вы можете запросить статус (tran.isFinished ()) или что-то подобное, вам не нужнобул.Любое выброшенное исключение (т. Е. Runtimeexception или ошибка, если нет проверенных исключений) просто пролетит мимо, выполняя блок finally на своем пути вверх по стеку.

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

3 голосов
/ 22 мая 2018

В Java 7 и более поздних версиях вы теперь можете повторно генерировать исключение, которое вы ловите, даже если тип не объявлен. (Это тоже было для меня новостью.) См. https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html и Почему в некоторых случаях допустимо повторно бросать Throwable, не объявляя его? .

Кроме того, не хотите ли вы поймать более общий Throwable? Конечно, возможно, они представляют некоторые проблемы во время выполнения, от которых невозможно справиться. Но не следует ли нам попытаться откатить транзакцию?

Еще одно соображение заключается в том, что сама команда отката может генерировать исключения, и вы не хотите, чтобы она переопределяла исходное исключение. Таким образом, вы должны добавить сгенерированное очисткой исключение как исключенное исключение , также добавленное в Java 7 как часть try-with-resources.

Поэтому мне кажется, что наилучшая практика для современного программирования JDBC будет следующей (с использованием чистого JDBC):

protected void doIt() {
  connnection.setAutoCommit(false);  //start transaction
  try {
    //TODO do stuff
    connection.commit();  //commit transaction
  } catch (final Throwable throwable) {
    try {
      connection.rollback();  //roll back transaction
    } catch (final SQLException sqlException) {
      throwable.addSuppressed(sqlException);
    }
    throw throwable;  //should now be allowed in Java 7+
  }      
}

Пожалуйста, дайте мне знать, что я пропустил или не учел.

1 голос
/ 13 января 2010

Если вы используете Java 5+, среда Spring имеет простую аннотацию (@Transactional), которую вы можете использовать для настройки транзакций.

это фрагмент того, как выглядит начало вашего кода, если вы хотите откатить любое исключение:

import org.springframework.transaction.annotation.Transactional;

@Transactional(rollbackFor = Exception.class)
protected void doIt()

Следующая ссылка может помочь вам начать работу, если вы хотите использовать это: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html. Раздел 9.5.6 - это специальный раздел для использования @ Transactional.

...