Spring транзакция: неожиданное поведение отката - PullRequest
0 голосов
/ 13 июня 2018

Я делаю простой эксперимент для отладки.

Сначала я вставляю несколько записей в базу данных, а затем делаю недопустимое преобразование данных, которое вызывает исключение DataIntegrityViolationException, но я поймаю исключение.

Я ожидал, что записи будут успешно вставлены в базу данных, так как я ловлю проверенное исключение.Но все это откатывается.

Я делаю эксперимент снова, используя TransactionTemplate вместо аннотации, тот же результат.

Мои вопросы:

  1. Это ожидаемое поведение?
  2. Если ответ на вопрос №1 - да, тогда я улавливаю исключение, как это возможно?что весна знает, что исключение брошено?

Вот мой код:

public void insertValue() {
    jdbcTemplate.execute("insert into people (person_id, name) values (4, 'asjkdhadsjkqhweqkewhkashdkahd')");
    jdbcTemplate.execute("insert into people (person_id, name) values (5, 'tttqqq')");
}
// this should throw exception
public void truncateValue() {
    jdbcTemplate.execute("alter table people alter column name varchar(7)");
}

public void jdbc_calls() {
    insertValue();
    try {
        truncateValue();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
    System.out.println("Finish");
}

public void run() {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

    transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);

    transactionTemplate.execute(transactionStatus -> {
        try {
            jdbc_calls();
        } catch (RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return null;
    });
}

Подробнее о вопросе №2.Вот исходный код TransactionTemplate.execute (). Насколько я понимаю, если я не сгенерирую исключение, сработает rollbackOnException.

public <T> T execute(TransactionCallback<T> action) throws TransactionException {
    Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

    if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
        return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
    }
    else {
        TransactionStatus status = this.transactionManager.getTransaction(this);
        T result;
        try {
            result = action.doInTransaction(status);
        }
        catch (RuntimeException | Error ex) {
            // Transactional code threw application exception -> rollback
            rollbackOnException(status, ex);
            throw ex;
        }
        catch (Throwable ex) {
            // Transactional code threw unexpected exception -> rollback
            rollbackOnException(status, ex);
            throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
        }
        this.transactionManager.commit(status);
        return result;
    }
}

1 Ответ

0 голосов
/ 13 июня 2018
  1. это ожидаемое поведение?

Да, это так.

Если ответ № 1 - «да», то я улавливаю исключение. Как возможно, что Spring знает, что было сгенерировано исключение?

Когда происходит исключение, Spring будет отмечатьВаша транзакция как rollbackOnly.Таким образом, даже когда вы перехватываете свое исключение, в конце вашего метода ваша транзакция все еще откатывается.

В вашем случае я не понимаю, почему вы используете @Transaction, так как вы хотите зафиксировать независимо от исключенияпроисходит.

Редактировать

Когда вы используете транзакцию с БД, вызов транзакции делегируется EntityManager.

Посмотрите на AbstractEntityManagerImpl#handlePersistenceException:

@Override
public void handlePersistenceException(PersistenceException e) {
    if ( e instanceof NoResultException ) {
        return;
    }
    if ( e instanceof NonUniqueResultException ) {
        return;
    }
    if ( e instanceof LockTimeoutException ) {
        return;
    }
    if ( e instanceof QueryTimeoutException ) {
        return;
    }

    try {
        markForRollbackOnly();
    }
    catch ( Exception ne ) {
        //we do not want the subsequent exception to swallow the original one
        LOG.unableToMarkForRollbackOnPersistenceException(ne);
    }
}

При возникновении исключения EntityManager помечает вашу транзакцию как rollbackOnly до того, как выбрасывает исключение, которое вы хотите перехватить.

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

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

javax.persistence.RollbackException: транзакция, помеченная как rollbackOnly

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