您的位置:首页 > 房产 > 家装 > 做网站优化公司排行_行业网站建设_石家庄seo优化_谷歌在线浏览入口

做网站优化公司排行_行业网站建设_石家庄seo优化_谷歌在线浏览入口

2025/4/22 4:33:07 来源:https://blog.csdn.net/qq_74212030/article/details/147163038  浏览:    关键词:做网站优化公司排行_行业网站建设_石家庄seo优化_谷歌在线浏览入口
做网站优化公司排行_行业网站建设_石家庄seo优化_谷歌在线浏览入口

什么是事务?

事务就是用户定义的一系列执行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 管理
  • 数据库不支持事务

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com