• Spring系列九:Spring 事务


    该文章收录专栏:

    Spring系列专栏

    主页:

    新星计划第三季人工智能赛道TOP2;退役复学在校大学生,全栈JAVA领域创作者,目光所至,皆为华夏

    目录

    ?

    什么是事务?

    23.Spring 事务的种类?

    24.Spring 的事务隔离级别?

    25.Spring 的事务传播机制?

    26.声明式事务实现原理了解吗?

    27.声明式事务在哪些情况下会失效?


    什么是事务?

    Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的。Spring 只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过数据库自己的事务机制实现。接下来由带领你们学习Spring事务~

    23.Spring 事务的种类?

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

    Spring事务分类

    1. 编程式事务

    编程式事务管理使用 TransactionTemplate,需要显式执行事务。

    1. 声明式事务

    2. 声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务

    3. 优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

    24.Spring 的事务隔离级别?

    Spring的接口TransactionDefinition中定义了表示隔离级别的常量,当然其实主要还是对应数据库的事务隔离级别:

    1. ISOLATION_DEFAULT:使用后端数据库默认的隔离界别,MySQL 默认可重复读,Oracle 默认读已提交。

    2. ISOLATION_READ_UNCOMMITTED:读未提交

    3. ISOLATION_READ_COMMITTED:读已提交

    4. ISOLATION_REPEATABLE_READ:可重复读

    5. ISOLATION_SERIALIZABLE:串行化

    25.Spring 的事务传播机制?

    Spring 事务的传播机制说的是,当多个事务同时存在的时候——一般指的是多个事务方法相互调用时,Spring 如何处理这些事务的行为。

    事务传播机制是使用简单的 ThreadLocal 实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。

    7种事务传播机制

    Spring默认的事务传播行为是PROPAFATION_REQUIRED,它适合绝大多数情况,如果多个ServiceX#methodX()都工作在事务环境下(均被Spring事务增强),且程序中存在调用链Service1#method1()->Service2#method2()->Service3#method3(),那么这3个服务类的三个方法通过Spring的事务传播机制都工作在同一个事务中。

    26.声明式事务实现原理了解吗?

    就是通过AOP/动态代理。

    • 在Bean初始化阶段创建代理对象:Spring容器在初始化每个单例bean的时候,会遍历容器中的所有BeanPostProcessor实现类,并执行其postProcessAfterInitialization方法,在执行AbstractAutoProxyCreator类的postProcessAfterInitialization方法时会遍历容器中所有的切面,查找与当前实例化bean匹配的切面,这里会获取事务属性切面,查找@Transactional注解及其属性值,然后根据得到的切面创建一个代理对象,默认是使用JDK动态代理创建代理,如果目标类是接口,则使用JDK动态代理,否则使用Cglib。

    • 在执行目标方法时进行事务增强操作:当通过代理对象调用Bean方法的时候,会触发对应的AOP增强拦截器,声明式事务是一种环绕增强,对应接口为MethodInterceptor,事务增强对该接口的实现为TransactionInterceptor,类图如下:

      图片来源网易技术专栏

      事务拦截器TransactionInterceptorinvoke方法中,通过调用父类TransactionAspectSupportinvokeWithinTransaction方法进行事务处理,包括开启事务、事务提交、异常回滚。

    27.声明式事务在哪些情况下会失效?

    声明式事务的几种失效的情况

    1、@Transactional 应用在非 public 修饰的方法上

    如果Transactional注解应用在非 public 修饰的方法上,Transactional将会失效。

    是因为在Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的intercept方法 或 JdkDynamicAopProxy的invoke方法会间接调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法,获取Transactional 注解的事务配置信息。

    protected?TransactionAttribute?computeTransactionAttribute(Method?method,
    ????Class?targetClass)?{
    ????????//?Don't?allow?no-public?methods?as?required.
    ????????if?(allowPublicMethodsOnly()?&&?!Modifier.isPublic(method.getModifiers()))?{
    ????????return?null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    2、@Transactional 注解属性 propagation 设置错误

    • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

    • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

    • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

    3、@Transactional 注解属性 rollbackFor 设置错误

    rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException的异常)或者 Error才回滚事务,其他异常不会触发回滚事务。

    Spring默认支持的异常回滚

    //?希望自定义的异常可以进行回滚
    @Transactional(propagation=?Propagation.REQUIRED,rollbackFor=?MyException.class
    
    • 1
    • 2

    若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。

    4、同一个类中方法调用,导致@Transactional失效

    开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

    那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

    ?//@Transactional
    ?????@GetMapping("/test")
    ?????private?Integer?A()?throws?Exception?{
    ?????????CityInfoDict?cityInfoDict?=?new?CityInfoDict();
    ?????????cityInfoDict.setCityName("2");
    ?????????/**
    ??????????*?B?插入字段为?3的数据
    ??????????*/
    ?????????this.insertB();
    ????????/**
    ?????????*?A?插入字段为?2的数据
    ?????????*/
    ????????int?insert?=?cityInfoDictMapper.insert(cityInfoDict);
    ????????return?insert;
    ????}
    
    ????@Transactional()
    ????public?Integer?insertB()?throws?Exception?{
    ????????CityInfoDict?cityInfoDict?=?new?CityInfoDict();
    ????????cityInfoDict.setCityName("3");
    ????????cityInfoDict.setParentCityId(3);
    
    ????????return?cityInfoDictMapper.insert(cityInfoDict);
    ????}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这种情况是最常见的一种@Transactional注解失效场景

    @Transactional
    private?Integer?A()?throws?Exception?{
    ????int?insert?=?0;
    ????try?{
    ????????CityInfoDict?cityInfoDict?=?new?CityInfoDict();
    ????????cityInfoDict.setCityName("2");
    ????????cityInfoDict.setParentCityId(2);
    ????????/**
    ?????????*?A?插入字段为?2的数据
    ?????????*/
    ????????insert?=?cityInfoDictMapper.insert(cityInfoDict);
    ????????/**
    ?????????*?B?插入字段为?3的数据
    ????????*/
    ????????b.insertB();
    ????}?catch?(Exception?e)?{
    ????????e.printStackTrace();
    ????}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务就不能正常回滚了,会抛出异常:

    org.springframework.transaction.UnexpectedRollbackException:?Transaction?rolled?back?because?it?has?been?marked?as?rollback-only
    
    • 1

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    vue 生命周期钩子函数 created()案例
    基于javaweb的大学生兼职系统(java+springboot+jsp+mysql)
    解决rsyslog服务占用内存过高
    BeanFactory和ApplicationContext
    当zk某个节点坏掉如何修复
    Spring中@Controller 和 @RestController 的作用与区别
    链的解构主义:一览 9 大模块化公链
    springboot(ssm 拍卖行系统 在线拍卖平台 Java(code&LW)
    坐山观虎斗,四两拨千斤
    1388. 3n 块披萨
  • 原文地址:https://blog.csdn.net/m0_67394230/article/details/126117240