Spring系列九:Spring 事务

简介: ​目录 什么是事务?什么是事务?Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的。Spring 只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过数据库自己的事务机制实现。接下来由叶秋学长带领你们学习Spring事务~23.Spring 事务的种类?Spring 支持编程式事务管理和声明式事务管理两种方式:Spring事务分类编程式事务管理使用 TransactionTemplate,需要显式执行事务。24.Spring 的事务隔离级别?

  该文章收录专栏:

叶秋学长Spring系列专栏

叶秋学长主页:

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

目录

 

什么是事务?

23.Spring 事务的种类?

24.Spring 的事务隔离级别?

25.Spring 的事务传播机制?

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

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


什么是事务?

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

23.Spring 事务的种类?

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

image.gif编辑

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 实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。

        image.gif编辑

        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,类图如下:


            image.gif编辑图片来源网易技术专栏
            事务拦截器TransactionInterceptorinvoke方法中,通过调用父类TransactionAspectSupportinvokeWithinTransaction方法进行事务处理,包括开启事务、事务提交、异常回滚。

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

          image.gif编辑

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

          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;
          }

          image.gif

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

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

            • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
            • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
            • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

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

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

            image.gif编辑

            Spring默认支持的异常回滚

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

            image.gif

            若在目标方法中抛出的异常是 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);
                }

            image.gif

            这种情况是最常见的一种@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();
                }
            }

            image.gif

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

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

            image.gif


            相关文章
            |
            23天前
            |
            Java 关系型数据库 数据库
            深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
            在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
            |
            6月前
            |
            Java Spring
            Spring中事务失效的场景
            因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是被代理对象调用, 那么@Transactional会失效 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效 如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
            |
            6月前
            |
            Java 关系型数据库 数据库
            微服务——SpringBoot使用归纳——Spring Boot事务配置管理——常见问题总结
            本文总结了Spring Boot中使用事务的常见问题,虽然通过`@Transactional`注解可以轻松实现事务管理,但在实际项目中仍有许多潜在坑点。文章详细分析了三个典型问题:1) 异常未被捕获导致事务未回滚,需明确指定`rollbackFor`属性;2) 异常被try-catch“吃掉”,应避免在事务方法中直接处理异常;3) 事务范围与锁范围不一致引发并发问题,建议调整锁策略以覆盖事务范围。这些问题看似简单,但一旦发生,排查难度较大,因此开发时需格外留意。最后,文章提供了课程源代码下载地址,供读者实践参考。
            133 0
            |
            6月前
            |
            Java 关系型数据库 数据库
            微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
            本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
            868 0
            |
            6月前
            |
            Java 数据库 微服务
            微服务——SpringBoot使用归纳——Spring Boot事务配置管理——事务相关
            本文介绍Spring Boot事务配置管理,阐述事务在企业应用开发中的重要性。事务确保数据操作可靠,任一异常均可回滚至初始状态,如转账、购票等场景需全流程执行成功才算完成。同时,事务管理在Spring Boot的service层广泛应用,但根据实际需求也可能存在无需事务的情况,例如独立数据插入操作。
            129 0
            |
            4月前
            |
            人工智能 Java 数据库连接
            Spring事务失效场景
            本文深入探讨了Spring框架中事务管理可能失效的几种常见场景及解决方案,包括事务方法访问级别不当、方法内部自调用、错误的异常处理、事务管理器或数据源配置错误、数据库不支持事务以及不合理的事务传播行为或隔离级别。通过合理配置和正确使用`@Transactional`注解,开发者可以有效避免这些问题,确保应用的数据一致性和完整性。
            221 10
            |
            3月前
            |
            Java 关系型数据库 MySQL
            【Spring】【事务】初学者直呼学会了的Spring事务入门
            本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
            259 1
            |
            6月前
            |
            SQL Java 数据库连接
            Spring中的事务是如何实现的
            1. Spring事务底层是基于数据库事务和AOP机制的 2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean 3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步 6. 然后执⾏当前⽅法,⽅法中会执⾏sql 7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务
            |
            6月前
            |
            JavaScript Java 开发者
            Spring事务失效,常见的情况有哪些?
            本文总结了Spring事务失效的7种常见情况,包括未启用事务管理功能、方法非public类型、数据源未配置事务管理器、自身调用问题、异常类型错误、异常被吞以及业务和事务代码不在同一线程中。同时提供了两种快速定位事务相关Bug的方法:通过查看日志(设置为debug模式)或调试代码(在TransactionInterceptor的invoke方法中设置断点)。文章帮助开发者更好地理解和解决Spring事务中的问题。
            214 7
            |
            8月前
            |
            SQL Java 关系型数据库
            【SpringFramework】Spring事务
            本文简述Spring中数据库及事务相关衍伸知识点。
            102 9