代码之家  ›  专栏  ›  技术社区  ›  user2628641

spring事务:意外的回滚行为

  •  1
  • user2628641  · 技术社区  · 6 年前

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

    首先我将一些记录插入数据库,然后执行无效的数据转换,这将引发DataIntegrityViolationException,但我将捕获异常。

    我希望记录成功插入数据库,因为我捕获了checked异常。但整件事都被退回去了。

    我再次使用transactiontemplate而不是使用annotation进行实验,结果相同。

    我的问题是:

    1. 这是预期的行为吗?
    2. 如果对no.1的回答是yes,那么我捕捉到异常,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;
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Mạnh Quyết Nguyễn    6 年前
    1. 这是预期的行为吗?

    是的,是的。

    1. 如果对no.1的回答是yes,那么我捕捉到异常,spring怎么可能知道抛出了异常?

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

    对你来说,我不明白你为什么 @Transaction 因为不管是否发生异常,都要提交。

    编辑

    当您在db中使用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将您的事务标记为 滚回式 之前抛出异常让你抓到。

    在您的服务中捕捉到异常之后, AbstractPlatformTransactionManager 将尝试提交(因为,如您所知,没有检测到异常),但EntityManager拒绝提交,因为它检测到事务标记为仅回滚。

    如果您阅读异常,您将看到如下内容:

    javax.persistence.rollbackexception:标记为rollbackonly的事务