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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 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事务的执行流程

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
9天前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
2月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
|
2月前
|
监控 架构师 NoSQL
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
|
4月前
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
160 32
|
4月前
|
人工智能 Java 数据库连接
Spring事务失效场景
本文深入探讨了Spring框架中事务管理可能失效的几种常见场景及解决方案,包括事务方法访问级别不当、方法内部自调用、错误的异常处理、事务管理器或数据源配置错误、数据库不支持事务以及不合理的事务传播行为或隔离级别。通过合理配置和正确使用`@Transactional`注解,开发者可以有效避免这些问题,确保应用的数据一致性和完整性。
185 10
|
3月前
|
Java 关系型数据库 MySQL
【Spring】【事务】初学者直呼学会了的Spring事务入门
本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
223 1
|
4月前
|
Java 开发者 Spring
Spring框架 - 深度揭秘Spring框架的基础架构与工作原理
所以,当你进入这个Spring的世界,看似一片混乱,但细看之下,你会发现这里有个牢固的结构支撑,一切皆有可能。不论你要建设的是一座宏大的城堡,还是个小巧的花园,只要你的工具箱里有Spring,你就能轻松搞定。
169 9
|
3月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
428 0
|
5月前
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
216 0
|
XML Java 数据格式
Spring面试底层原理的那些问题,你是不是真的懂Spring?
1、什么是 Spring 框架?Spring 框架有哪些主要模块?Spring 框架是一个为 Java 应用程序的开发提供了综合、广泛的基础性支持的 Java 平台。Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。
1544 0