当前位置: 首页 > news >正文

说一说 Spring 中的事务

什么是事务?

事务就是用户定义的一系列执行SQL语句的操作, 这些操作要么完全地执行,要么完全地都不执行, 它是一个不可分割的工作执行单元。

Spring 中的事务是怎么实现的?

  • Spring事务底层是基于数据库事务和AOP机制的
  • 首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
  • 当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解
  • 如果加了,那么则利用事务管理器创建一个数据库连接
  • 并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步
  • 然后执行当前方法,方法中会执行sql
  • 执行完当前方法后,如果没有出现异常就直接提交事务
  • 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
  • Spring事务的隔离级别对应的就是数据库的隔离级别
  • Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的
  • Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个数据库连接,在此新数据库连接上执行sql
    在这里插入图片描述

如何理解Spring事务的传播机制的?

在这里插入图片描述

一个线程在运行过程中,可能会连续调用好几个方法,在调用某一个方法时,可能就开启了一个Spring事务,那么在调用接下来的方法时,到底是共用同一个事务呢?还是新开一个事务呢?这就是传播机制,程序员可以根据不同的业务场景进行配置,比如:

  • REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
  • SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
  • MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
  • REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务。
  • NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
  • NEVER:不使用事务,如果当前事务存在,则抛出异常
  • NESTED:如果当前事务存在,则在嵌套事务中执行,否则和REQUIRED的操作一样(开启一个事务)

Spring 是如何实现事务的?

在Spring中,一个Spring事务就是对应一个数据库连接,新开一个Spring事务其实就是新开一个数据库连接,比如执行某个方法时需要开启一个Spring事务,那么就会创建一个数据库连接,然后开始执行方法,如果方法中调用了其他方法,此时就会看这个其他方法怎么配置的:

  • 比如是REQUIRES_NEW,那么就会新开一个数据库连接,这个其中方法中的sql就会在这个新开的数据库连接中执行
  • 比如是REQUIRED,那么就不会新开,而是基于之前的数据库连接来执行方法中的sql

那种情况会导致事务失效?

  • 方法内的自调用:Spring事务是基于AOP的,只要使用代理对象调用某个方法时,Spring事务才能生效,而在一个方法中调用使用this.xxx()调用方法时,this并不是代理对象,所以会导致事务失效。
    • 解放办法1:把调用方法拆分到另外一个Bean中
    • 解决办法2:自己注入自己
    • 解决办法3:AopContext.currentProxy()+@EnableAspectJAutoProxy(exposeProxy = true)
  • 方法是被 private 修饰的,Spring事务会基于CGLIB来进行AOP,而CGLIB会基于父子类来失效,子类是代理类,父类是被代理类,如果父类中的某个方法是private的,那么子类就没有办法重写它,也就没有办法额外增加Spring事务的逻辑
  • 方法是final的:原因和private是一样的,也是由于子类不能重写父类中的final的方法

tip: 这种说法有待考察 建议不要回答这个

  • 单独的线程调用方法:当Mybatis或JdbcTemplate执行SQL时,会从ThreadLocal中去获取数据库连接对象,如果开启事务的线程和执行SQL的线程是同一个,那么就能拿到数据库连接对象,如果不是同一个线程,那就拿到不到数据库连接对象,这样,Mybatis或JdbcTemplate就会自己去新建一个数据库连接用来执行SQL,此数据库连接的autocommit为true,那么执行完SQL就会提交,后续再抛异常也就不能再回滚之前已经提交了的SQL了
  • 没加@Configuration注解:如果用SpringBoot基本没有这个问题,但是如果用的Spring,那么可能会有这个问题,这个问题的原因其实也是由于Mybatis或JdbcTemplate会从ThreadLocal中去获取数据库连接,但是ThreadLocal中存储的是一个MAP,MAP的key为DataSource对象,value为连接对象,而如果我们没有在AppConfig上添加@Configuration注解的话,会导致MAP中存的DataSource对象和Mybatis和JdbcTemplate中的DataSource对象不相等,从而也拿不到数据库连接,导致自己去创建数据库连接了
  • 异常被吃掉:如果Spring事务没有捕获到异常,那么也就不会回滚了,默认情况下Spring会捕获RuntimeException和Error
  • 类没有被 Spring 管理
  • 数据库不支持事务

小总结,事务的传播机制这里被问到还是挺多的,所以可以重点看

相关文章:

  • AI——seaborn使用
  • 【位运算】两整数之和
  • Verilog:LED呼吸灯
  • 【android bluetooth 框架分析 02】【Module详解 2】【gd_shim_module 模块介绍】
  • linux 内存踩踏导致的空指针问题分析纪要
  • 湖北建筑安全员C1证考试难度怎么样
  • Linux 第三讲 --- 基础指令(三)
  • 智能医疗辅助诊断:深度解析与实战教程
  • 免费干净!付费软件的平替款!
  • 基于 Spring Boot + Vue 的 [业务场景] 管理系统设计与实现
  • 微软VSCode 能否击败 Cursor 和 Windsurf?
  • 【仪器仪表专题】案例:怎么理解电池模拟器模拟电池内阻的功能
  • 用户登录不上linux服务器
  • AMBA-CHI协议详解(二十六)
  • docker容器安装的可道云挂接宿主机的硬盘目录:解决群晖 威联通 飞牛云等nas的硬盘挂接问题
  • 淘宝 API 接口开发最佳实践:商品详情数据抓取与错误处理方案
  • Android之JNI详解
  • IntelliJ IDEA历史版本下载安装链接
  • MCP基础学习五:MCP的优化与高级功能
  • RAG创建向量数据库:docsearch = FAISS.from_texts(documents, embeddings)
  • 影视公司起名/天津优化加盟
  • 广州购物商城网站开发/佛山百度seo点击软件
  • 做网站的规范/域名解析ip地址
  • 做网站_没内容/流量宝
  • 怎样做网站的优化 排名/上海优化seo排名
  • 好看的模板网站建设/站长工具seo综合查询广告