【问题标题】:Spring transaction: unexpected rollback behaviorSpring事务:意外的回滚行为
【发布时间】:2018-11-23 04:02:05
【问题描述】:

我正在做一个简单的调试实验。

首先我将多条记录插入数据库,然后进行无效的数据转换,这将引发 DataIntegrityViolationException,但我会捕获该异常。

我希望记录成功插入到数据库中,因为我捕获了检查的异常。但整个事情都回滚了。

我使用 TransactionTemplate 而不是使用注解再次进行实验,结果相同。

我的问题是:

  1. 这是预期的行为吗?
  2. 如果 No.1 的答案是肯定的,那么我捕获了异常,spring 怎么可能知道抛出了异常?

这是我的代码:

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;
    }
}

【问题讨论】:

    标签: spring spring-jdbc spring-transactions jdbctemplate


    【解决方案1】:
    1. 这是预期的行为吗?

    是的。

    1. 如果 No.1 的答案是肯定的,那么我捕获了异常,spring 怎么可能知道抛出了异常?

    当异常发生时,spring会将你的事务标记为rollbackOnly。 因此,即使您捕获了异常,在您的方法结束时,您的事务仍然会回滚。

    在你的情况下,我不明白你为什么使用@Transaction,因为无论是否发生异常你都想提交。

    编辑

    当您使用 DB 事务时,事务调用被委托给 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

    【讨论】:

    • 谢谢!你知道“spring 会将你的事务标记为 rollbackOnly”吗?我正在阅读 TransactionTemplate 的源代码,但找不到它....顺便说一句,此示例仅用于调试目的
    • 我试图在 AbstractEntityManagerImpl 中设置一个断点,但没有使用这个类。 TransactionTemplate.execute() 被调用
    • 所以你使用不同的事务管理器。想法是一样的。您的用例的类是不同的。明天我会带着你用例的课程回到你身边,现在我要睡觉了^^
    • 嘿,您已经将@Transaction 更改为TransactionTemplate
    猜你喜欢
    • 2012-04-13
    • 2017-11-21
    • 1970-01-01
    • 2011-11-21
    • 2021-10-15
    • 2016-09-19
    • 2017-03-03
    • 2012-03-10
    • 2018-07-15
    相关资源
    最近更新 更多