前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring的事务详解

Spring的事务详解

作者头像
终有救赎
发布2023-10-16 10:18:11
1490
发布2023-10-16 10:18:11
举报
文章被收录于专栏:多线程

事务在Spring中是如何运作的

在了解嵌套事务之前,可以先看下单个事务在Spring中的处理流程,以便后面可以更清晰地认识嵌套事务的逻辑。

在这里插入图片描述
在这里插入图片描述

Spring事务使用AOP的机制实现,会在@Transactional注解修饰的方法前后分别织入开启事务的逻辑,以及提交或回滚的逻辑。@Transactional可以修饰在方法或者类上,区别就在于修饰于类上的,会对该类下符合条件的方法(例如private修饰的方法就不符合条件)前后都织入事务的逻辑。

spring事务传播机制

声明式事务将事务管理代码从业务方法中抽离了出来,以声明式的方式来实现事务管理,对于开发者来说,声明式事务显然比编程式事务更易用、更好用。 当然了,要想实现事务管理和业务代码的抽离,就必须得用到 Spring 当中的AOP,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。 声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。

声明式

在配置文件中设置以下6项

required

如果客户端没有事务 在bean中新起一个事务

如果客户端有事务bean 中就加进去

子事务

主事务

结果

异常

正常,并try-catch异常

均回滚

正常

异常

均回滚

正常

异常,并try-catch异常

不回滚

requiresNew

不管客户端有没有事务服务器段都新起一个事务

如果客户端有事务就将事务挂起

子事务

主事务

结果

异常

正常,并try-catch异常

子回滚,主不回滚

正常

异常

子不回滚,主回滚

异常

正常

均回滚

supports

如果客户端没有事务服务端也没有事务

如果客户端有事务服务端就加一个事务

mandatcry

如果客户端没有事务服务端就会报错

如果客户端有事务服务端就加事务

notSupported

不管客户端有没有事务服务端都没有事务

如果客户端有事务服务端就挂起

never

不管客户端有没有事务服务端都没有事务

如果客户端有事务就报错

NESTED

如果当前存在事务,则在嵌套事务内执行。

如果当前没有事务,则进行与REQUIRED类似的操作

子事务

主事务

结果

异常

正常,并try-catch异常

子回滚,主不回滚

正常

异常

均回滚

异常

正常

均回滚

编程式事务

Javax.transaction.UserTranscation

通常情况下只需要一个 @Transactional 就搞定了(代码侵入性降到了最低),就像这样:

代码语言:javascript
复制
/**
 * 模拟转账
 */
@Transactional
public void handle() {
 // 转账
 transfer(double money);
 // 减自己的钱
  Reduce(double money);
}

编程式事务是指将事务管理代码嵌入嵌入到业务代码中,来控制事务的提交和回滚。 你比如说,使用 TransactionTemplate 来管理事务:

代码语言:javascript
复制
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}

再比如说,使用 TransactionManager 来管理事务:

代码语言:javascript
复制
@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

就编程式事务管理而言,Spring 更推荐使用 TransactionTemplate。 在编程式事务中,必须在每个业务操作中包含额外的事务管理代码,就导致代码看起来非常的臃肿,但对理解 Spring 的事务管理模型非常有帮助。

事务隔离级别

前面我们已经了解了数据库的事务隔离级别,再来理解 Spring 的事务隔离级别就容易多了。 TransactionDefinition 中一共定义了 5 种事务隔离级别:

ISOLATION_DEFAULT,使用数据库默认的隔离级别,MySql 默认采用的是 REPEATABLE_READ,也就是可重复读。 ISOLATION_READ_UNCOMMITTED,最低的隔离级别,可能会出现脏读、幻读或者不可重复读。 ISOLATION_READ_COMMITTED,允许读取并发事务提交的数据,可以防止脏读,但幻读和不可重复读仍然有可能发生。 ISOLATION_REPEATABLE_READ,对同一字段的多次读取结果都是一致的,除非数据是被自身事务所修改的,可以阻止脏读和不可重复读,但幻读仍有可能发生。 ISOLATION_SERIALIZABLE,最高的隔离级别,虽然可以阻止脏读、幻读和不可重复读,但会严重影响程序性能。 通常情况下,我们采用默认的隔离级别 ISOLATION_DEFAULT 就可以了,也就是交给数据库来决定。

事务的超时时间

事务超时 timeout ,也就是指一个事务所允许执行的最长时间,如果在超时时间内还没有完成的话,就自动回滚。 假如事务的执行时间格外的长,由于事务涉及到对数据库的锁定,就会导致长时间运行的事务占用数据库资源。

事务的只读属性

事务的只读属性readOnly, 如果一个事务只是对数据库执行读操作,那么该数据库就可以利用事务的只读属性,采取优化措施,适用于多条数据库查询操作中。 为什么一个查询操作还要启用事务支持呢? 这是因为 MySql(innodb)默认对每一个连接都启用了 autocommit 模式,在该模式下,每一个发送到 MySql 服务器的 SQL 语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务。 那如果我们给方法加上了 @Transactional 注解,那这个方法中所有的 SQL 都会放在一个事务里。否则,每条 SQL 都会单独开启一个事务,中间被其他事务修改了数据,都会实时读取到。 有些情况下,当一次执行多条查询语句时,需要保证数据一致性时,就需要启用事务支持。否则上一条 SQL 查询后,被其他用户改变了数据,那么下一个 SQL 查询可能就会出现不一致的状态。

事务的回滚策略

回滚策略rollbackFor,用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。默认情况下,事务只在出现运行时异常(Runtime Exception)时回滚,以及 Error,出现检查异常(checked exception,需要主动捕获处理或者向上抛出)时不回滚。

如果你想要回滚特定的异常类型的话,可以这样设置:

代码语言:javascript
复制
@Transactional(rollbackFor= MyException.class)

事务的不回滚策略

不回滚策略noRollbackFor,用于指定不触发事务回滚的异常类型,可以指定多个异常类型。

@Transaction失效场景

  • 作用于非public方法上,之所以会失效是因为在Spring AOP 代理时,如下图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute

方法,获取Transactional 注解的事务配置信息。 此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

注意:protected、private修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

  • propagation设置问题,会导致事务不生效,也就事务不会回滚
  • rollbackFor指定事务回滚的异常类型
  • 同个类中的调用被@transaction修饰的方法,会失效,因为只有当事务方法被当前类以外的代码调用,才会由spring生成的代理对象来管理。
  • try catch导致失效
  • 数据库不支持事务
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 事务在Spring中是如何运作的
  • spring事务传播机制
    • 声明式
      • required
      • requiresNew
      • supports
      • mandatcry
      • notSupported
      • never
      • NESTED
    • 编程式事务
    • 事务隔离级别
      • 事务的超时时间
        • 事务的只读属性
          • 事务的回滚策略
            • 事务的不回滚策略
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档