前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析spring声明式事务使用

浅析spring声明式事务使用

作者头像
山行AI
发布2019-07-23 14:16:44
5960
发布2019-07-23 14:16:44
举报
文章被收录于专栏:山行AI山行AI

关于spring事务主要有四种特性和五种隔离级别和7种传播行为,这篇文章就来好好总结一下。

1. 四种特性

  • 原子性 (atomicity):简单理解事物是可能是多个活动的工作单元要么全部发生要么全部不发生。
  • 一致性 (consistency):事务的执行的前后数据的完整性保持一致.
  • 隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰。
  • 持久性(durability) : 一旦事务完成,结果应当保存起来,在系统崩溃后可以恢复回来。

2. 一般配置

  • spring的xml中配置如下:

是在事务管理器中配置的,关于事务管理器有不明白的可以翻下之前的文章。

springboot中和注解形式的是在@Transactional注解中配置的(添加注解时添加这些):

除了添加@Transactional注解,还可以使用transactionManager手动处理:

3. spring事务隔离级别设置(对应数据库级别的隔离级别)

  • ISOLATION_DEFAULT 使用底层默认,与数据库设置的隔离级别保持一致
  • ISOLATIONREADUNCOMMITTED 允许事物读取其他并行事物还没提交的数据,会发生脏读、幻读和不可重复读。
  • ISOLATIONREADCOMMITTED 保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。在read_uncommitted的基础上可以防止脏读。
  • ISOLATIONREPEATABLEREAD 除了保证一个事务不能被另外一个事务读取未提交的数据之外,还能避免不可重复读。可以防止脏读和不可重复读。
  • ISOLATION_SERIALIZABLE 串行化会让事务顺序执行。能避免脏读、幻读和不可重复读。

关于脏读、幻读和不可重复读:

1.脏读: 一个事务读到了另一个事务未提交的数据。2.不可重复读: 一个事务读到了另一个事务已经提交的update的数据,导致多次查询结果不一致。当一个事务中有两次相同的查询操作时,第一个查询得到结果后,在第二个查询执行时这条结果被修改了,会导致在同一个事务中两个查询的结果不一致。3.幻读: 一个事务中读到另一个事务已经提交的插入数据导致多次查询结果不一致。当一个事务中有两次或多次相同的查询操作时,第一个查询得到的不存在的记录,第二个查询中却发现被另一个事务插入了,这就是幻读。

关于隔离级别,在spring中的设置: 在org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin方法中:

对应的org.springframework.jdbc.datasource.DataSourceUtils#prepareConnectionForTransaction方法:

4. spring事务的传播行为

业务方法在容器中运行

存在父事务(即事务嵌套)

不存在父事务

REQUIRED

在父事务中运行

自己新建一个事务

SUPPORTS

在父事务中运行

正常执行

MANDATORY

在父事务中运行

抛出异常

REQUIRES_NEW

新建事务,将父事务挂起(suspend)

自己新建事务

NOT_SUPPORTED

如果存在方法调用将父事务挂起,调用结束恢复

不会开启事务

NEVER

抛出 异常

正常执行

NESTED

它的事务和父事务是相依的,与父事务一起提交,一起回滚

自己新建

上面存在父事务的场景主要是sevice调用service或dao层调用dao层的情况。

下面我们看看在spring源码里面的对应设置: 在执行时确定是否需要新建事务会调用获取事务的方法org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction:

代码语言:javascript
复制
/**
     * This implementation handles propagation behavior. Delegates to
     * {@code doGetTransaction}, {@code isExistingTransaction}
     * and {@code doBegin}.
     * @see #doGetTransaction
     * @see #isExistingTransaction
     * @see #doBegin
     */
    @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();

        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();

        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }

        if (isExistingTransaction(transaction)) {//如果当前存在事务
            // Existing transaction found -> check propagation behavior to find out how to behave.
            //进入处理已经存在事务的处理方法
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }
        //----------------------下面都是当前不存在事务的处理逻辑---------------------------
        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to proceed.
        //当传播行为是PROPAGATION_MANDATORY时,如果没有父事务,则抛出异常
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                //创建子事务的方法
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            catch (RuntimeException ex) {
                resume(null, suspendedResources);
                throw ex;
            }
            catch (Error err) {
                resume(null, suspendedResources);
                throw err;
            }
        }
        else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                        "isolation level will effectively be ignored: " + definition);
            }
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }
  • 上面的方法主要有两点,是否存在父事务。
  • 如果存在父事务,则进入handleExistingTransaction方法,会按照上面表格上的存在父事务的情况各种传播行为下的事务逻辑进行处理。
  • 如果不存在父事务则进入下面的逻辑,针对TransactionDefinition.PROPAGATIONREQUIRED、TransactionDefinition.PROPAGATIONREQUIRESNEW、TransactionDefinition.PROPAGATIONNESTED这三种在父事务不存在时会新建事务,最终会进入newTransactionStatus方法。
  • PROPAGATIONREQUIRESNEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。
  • PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 四种特性
  • 2. 一般配置
  • 3. spring事务隔离级别设置(对应数据库级别的隔离级别)
  • 4. spring事务的传播行为
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档