暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring编程式事务

字痕随行 2019-11-24
258

我们经常用到的Spring事务声明方式是:

  1. 使用AOP切面声明事务。

  2. 使用注解@Transactional声明事务。


这次试验一下另外一种事务实现方式:编程式事务。


以SpringBoot项目为例,使用以下代码声明事务:

@Primary
@Bean(name = "transactionManager")
public DataSourceTransactionManager getDataSourceTransactionManager(@Qualifier("dataSource")DataSource dataSource) {
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(dataSource);
    return dataSourceTransactionManager;
}

@Primary
@Bean(name = "transactionTemplate")
public TransactionTemplate getTransactionTemplate(@Qualifier("transactionManager")DataSourceTransactionManager transactionManager) {
    TransactionTemplate transactionTemplate = new TransactionTemplate();
    transactionTemplate.setTransactionManager(transactionManager);
    return transactionTemplate;
}


关于TranscationTemplate的解释:

Template class that simplifies programmatic transaction demarcation and transaction exception handling.

模板类,它简化了编程事务的划分和事务的异常处理。


在需要开启事务时,使用方法如下:

int count = transactionTemplate.execute(new TransactionCallback<Integer>() {
    @Override
    public Integer doInTransaction(TransactionStatus status) {
        info.setId(RandomUtil.randomUUID());
        int count1 = qyReportInfoMapper.insert(info);
        info.setId(RandomUtil.randomUUID());
        int count2 = qyReportInfoMapper.insert(info);
        return count1 + count2;
    }
});


正常情况下,会在对应的表中插入两条记录,如下图:


如果在执行了一条数据插入操作之后,不变更主键,直接造成主键冲突,代码运行异常,就会导致事务回滚,代码如下:

int count = transactionTemplate.execute(new TransactionCallback<Integer>() {
    @Override
    public Integer doInTransaction(TransactionStatus status) {
        info.setId(RandomUtil.randomUUID());
        int count1 = qyReportInfoMapper.insert(info);
        int count2 = qyReportInfoMapper.insert(info);
        return count1 + count2;
    }
});


抛出异常原因:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'd9206b48-c595-4566-9e1a-c8e211ce2fef' for key 'PRIMARY'


事务回滚,一条数据都没有插入。


如果想在SpringMVC中使用,只需要如下声明即可:

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>


这次尝试了编程式事务的实现,其实是因为在阅读Flowable源码时,发现它与Spring整合时,就是基于TranscationTemplate实现的,关键的代码段如下:

public class SpringTransactionInterceptor extends AbstractCommandInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringTransactionInterceptor.class);

    protected PlatformTransactionManager transactionManager;

    public SpringTransactionInterceptor(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @Override
    public <T> execute(final CommandConfig config, final Command<T> command) {
        LOGGER.debug("Running command with propagation {}", config.getTransactionPropagation());


        // If the transaction is required (the other two options always need to go through the transactionTemplate),
        // the transactionTemplate is not used when the transaction is already active.
        // The reason for this is that the transactionTemplate try-catches exceptions and marks it as rollback.
        // Which will break nested service calls that go through the same stack of interceptors.

        int transactionPropagation = getPropagation(config);
        if (transactionPropagation == TransactionTemplate.PROPAGATION_REQUIRED && TransactionSynchronizationManager.isActualTransactionActive()) {
            return next.execute(config, command);

        } else {
            //主要的实现在这里
            TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
            transactionTemplate.setPropagationBehavior(transactionPropagation);
            return transactionTemplate.execute(status -> next.execute(config, command));

        }

    }

    private int getPropagation(CommandConfig config) {
        switch (config.getTransactionPropagation()) {
        case NOT_SUPPORTED:
            return TransactionTemplate.PROPAGATION_NOT_SUPPORTED;
        case REQUIRED:
            return TransactionTemplate.PROPAGATION_REQUIRED;
        case REQUIRES_NEW:
            return TransactionTemplate.PROPAGATION_REQUIRES_NEW;
        default:
            throw new FlowableIllegalArgumentException("Unsupported transaction propagation: " + config.getTransactionPropagation());
        }
    }
}


以上,欢迎指正和探讨。

觉的不错?可以关注我的公众号↑↑↑


文章转载自字痕随行,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论