深入分析Spring事务和底层原理

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 深入分析Spring事务和底层原理

1 知识回顾

1.1 事务特性

MySQL事务特性

1.2 隔离级别

MySQL隔离级别

1.3 脏读、幻读、不可重复读

MySQL脏读、幻读、不可重复读

2 Spring使用事务的两种方式

2.1 编程式事务

使用TransactionalTemplate

@Autowired
private UserDAO userDAO;
@Autowired
private TransactionTemplate transactionTemplate;
@Override
public void insertUser(User user) {
    transactionTemplate.execute(new TransactionCallback<Object>() {
        //异常不需要处理,否则不回滚
        @Override
        public Object doInTransaction(TransactionStatus status) {
            userDAO.insertUser(user);
            return status;
        }
    });
}
2.3 声明式事务

使用@Transactional注解

@Transactional(rollbackFor = Exception.class)
@Override
public int updateUserById(User user) throws Exception {
    int res = userDAO.updateUserById(user);
    return res;
}
2.4 事务的传播机制

配置:@Transactional(propagation = Propagation.xxx)

public enum Propagation {
   /**
    * 支持当前事务,如果不存在则创建一个新的事务(Spring默认的事务传播机制).
    */
   REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
   /**
    * 支持当前事务,如果不存在则以非事务方式执行.
    */
   SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
   /**
    * 支持当前事务,如果不存在则抛出异常.
    */
   MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
   /**
    * 创建一个新事务,如果存在当前事务,则挂起当前事务.
    */
   REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
   /**
    * 以非事务方式执行,如果存在当前事务,则挂起当前事务  
    */
   NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
   /**
    * 以非事务方式执行,如果存在事务则抛出异常  .
    */
   NEVER(TransactionDefinition.PROPAGATION_NEVER),
   /**
    * 如果存在当前事务,则在嵌套事务中执行  ,否则,行为像{@code REQUIRED}.
    */
   NESTED(TransactionDefinition.PROPAGATION_NESTED);
    
   ...
}
  • REQUIRED:默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行。
  • REQUES_NEW:该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可。
  • SUPPORT:如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务。
  • NOT_SUPPORT:该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码。
  • NEVER:该传播机制不支持外层事务,即如果外层有事务就抛出异常。
  • MANDATORY:与NEVER相反,如果外层没有事务,则抛出异常。
  • NESTED:该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
2.5 事务的隔离级别

配置:@Transactional(isolation = Isolation.xxx)

ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。
ISOLATION_READ_COMMITTED (Oracle 默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。
ISOLATION_REPEATABLE_READ (MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
ISOLATION_SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
2.6 Spring事务回滚(声明式为例)

配置:@Transactional(rollbackFor = Exception.class)

含义:当出现rollbackFor 指定的异常类或者其派生类时才会触发事务回滚机制

2.6.1 Java异常类继承体系(部分)

Spring框架的事务管理默认是只在发生不受控异常(RuntimeException和Error)时才进行事务回滚。

2.6.1 事务回滚实验

(1)使用默认配置,抛出ClassNotFound异常查看是否回滚

@Transactional
@Override
public int updateUserById(User user) throws Exception {
    int res = userDAO.updateUserById(user);
    throw new ClassNotFoundException();
}

测试:

@Test
void contextLoads() {
    System.out.println("before update "+userService.selectUserById(1));
    try {
        userService.updateUserById(new User(1L, "aaa", "aaa"));
    } catch (Exception e) {
        System.err.println("---------捕获异常咯---------");
        System.out.println("after update "+userService.selectUserById(1));
    }
}

结果:

(2)配置成Excepction.class,抛出ClassNotFound异常查看是否回滚

@Transactional(rollbackFor = Exception.class)
@Override
public int updateUserById(User user) throws Exception {
    int res = userDAO.updateUserById(user);
    throw new ClassNotFoundException();
}

测试:

@Test
void contextLoads() {
    System.out.println("before update "+userService.selectUserById(1));
    try {
        userService.updateUserById(new User(1L, "bbb", "bbb"));
    } catch (Exception e) {
        System.err.println("---------捕获异常咯---------");
        System.out.println("after update "+userService.selectUserById(1));
    }
}

结果:

(3)使用默认配置,抛出NullPointException异常查看是否回滚

@Transactional
@Override
public int updateUserById(User user) throws Exception {
    int res = userDAO.updateUserById(user);
    throw new NullPointerException();
}

测试:

@Test
void contextLoads() {
    System.out.println("before update "+userService.selectUserById(1));
    try {
        userService.updateUserById(new User(1L, "ccc", "ccc"));
    } catch (Exception e) {
        System.err.println("---------捕获异常咯---------");
        System.out.println("after update "+userService.selectUserById(1));
    }
}

结果:

3 底层原理

3.1 概述

Spring事务属于AOP范畴,是通过代理对象对数据库的操作来进行事务处理,并且Spring事务的底层也是需要数据库的支持。

下面让我们debug下:

由此可见,声明事务之后,IOC初始化时会利用cglib进行AOP动态代理,将userDAO转变为代理对象对事务进行操作

点进入看下,并没有直接调用UserDAO的相关方法,而是进入了CglibAopProxy类进行对被调用对象的代理

继续向下debug直到出现异常

出现事务的相关信息,该步骤叫做事务清理

protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
   if (txInfo != null) {
      // 恢复之前的状态
      txInfo.restoreThreadLocalStatus();
   }
}
3.2 事务流程

由此,我们可以总结出Spring事务的执行流程

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
36 0
|
10天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
55 14
|
24天前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
42 2
Spring高手之路26——全方位掌握事务监听器
|
25天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
53 14
|
26天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
1月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
1月前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
|
1月前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
58 3
|
7月前
|
监控 Java 数据库
Spring事务相关配置、案例:转账业务追加日志及事务传播行为
Spring事务相关配置、案例:转账业务追加日志及事务传播行为
84 0
|
1月前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
56 1
Spring高手之路24——事务类型及传播行为实战指南