• SpringBoot的事务详解


    一、事务的存在目的

    1. 事务是为了解决数据安全问题而存在的。

    最经典的例子就是银行转账问题,账户A给账户B转账100元,账户A扣除100元之后由于不可抗力因素导致程序中断,账户B没有收到100元的转账,账户A的100元凭空消失,肯定是不行的。A扣款和B收款操作要么同时成功,要么同时失败,这个时候就需要引入事务操作。

    2、事务的四个特性

    • 原子性:一个事务是一个不可分割的工作单位。
    • 一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的。
    • 隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    • 持久性:一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

    3、事务管理方式

    Spring支持编程式事务管理和声明式事务管理两种方式。

    编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,Spring推荐使用TransactionTemplate。

    声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional 注解的方式),便可以将事务规则则应用到业务逻辑中。

    4、@Transactional注解

    它是声明式事务管理编程中使用的注解,放在接口实现类或接口实现方法上,并且只对public方法才起作用,一般在访问数据库的Service方法上添加。默认情况下,该注解只对 RuntimeException 及其子类异常执行事务回滚。 只读的接口不需要事务管理,防止影响系统性能。

    @Transactional 实质是使用了JDBC的事务来进行事务控制的,实现原理:

    4.1 事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,执行所有数据库命令。[不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接 connection 逻辑上新建一个会话session;DataSource 与 TransactionManager 配置相同的数据源)

    4.2 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,然后关闭该代理 connection 对象。(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

    1. public interface AccountService {
    2. @Transactional
    3. void transfer(String out,String in,Double money);
    4. }

    二、常见问题

    @EnableTransactionManagement
    问题:springboot 项目中是否需要在 启动类上添加 @EnableTransactionManagement 注解?
    答案:不需要。因为 springboot 自动装配已经帮助我们处理了,springboot 项目默认支持了事务。
    2. @Transactional 失效
    问题:方法上添加了 @Transactional 注解,为什么没有进行回滚?
    排查一:是否使用了 try catch 进行了异常捕获,并且捕获之后,没有通过 throw new RuntimeException(); 进行异常抛出。因为对于 spring aop 异常捕获原理,被拦截的方法需要显示的抛出异常,并不能进行任何处理,这样 aop 代理才能捕获到方法的异常,才能进行事务的回滚操作;默认清空下,aop 只捕获 RuntimeException 的异常,但是可以通过配置来捕获特定的异常并回滚。
    排查二:是否使用了 try catch 进行了异常信息捕获,如果是可以在 catch 语句中添加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 
    排查三:排查是不是产生了自调用的问题。在 spring 的 aop 代理下,只有目标方法被外部调用,目标方法才由 spring 生成的代理对象来管理;若统一类中的其他没有用 @Transactional 注解进行修饰的方法内部调用了 用 @Transactional 注解进行修饰的方法,有 @Transactional 注解的方法的事务将会被忽略,不发生回滚。

    备注:如果确实需要这样操作,只需要把 @Transactional 注解加在当前的类名上就可以了 或者使用 AspectJ 取代 spring aop 进行代理。
    排查四:@Transactional 注解只被应用到 public 修饰的方法上;如果在 protected、private等修饰的方法上,@Transactional 注解不会报错,但是这个注解的将不会生效。
    排查五:@Transactional 注解不会对当前修饰的方法的子方法生效。比如:我们在方法 A 中声明了 @Transactional 注解,但是 A 方法的内部调用的 方法 B 和 方法 C,其中方法 B 进行了 数据库的操作,但是该部分的异常被方法 B 进行了处理并且没有进行 抛出,这样的话事务是不会生效的。反之,如果 方法 B 声明了 @Transactional,但是方法 A 没有声明 @Transactional,A 方法内部调用 B 方法,事务也是不会生效的。如果想要事务生效,需要将子方法的事务控制交给调用的方法,在子方法中使用 @Transactional注解并通过 rollbackFor 指定定回滚的异常或者直接将异常 抛出。

    备注:在使用事务的时候,最好把子方法的异常进行抛出,交给调用的方法进行处理。

  • 相关阅读:
    服务案例|故障频发的一周,居然睡得更香!
    pip安装skimage的方法
    网络安全CTF流量分析-入门3-Webshell连接流量分析
    Mac Mini 安装Ubuntu20.04 KVM
    node exec(“clip“) 复制到剪切板 乱码
    Markdown还能这么玩?这款开源神器绝了~
    力扣第310场周赛总结与反思
    Linux动态库*.so函数名修改
    Golang标准库限流器rate使用
    PAT(甲级)2022年夏季考试
  • 原文地址:https://blog.csdn.net/weixin_51725434/article/details/127655193