禅与计算机 禅与计算机
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础

  • 并发编程

  • JVM相关

  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 深入解读 Spring MVC:Web 开发的得力助手
    • 探索 Spring 事务的奥秘
      • 写在文章开头
      • 详解Spring中的事务
        • 什么是事务
        • 事务的特性ACID简介
        • Spring支持的两种事务管理
        • Spring事务隔离级别和传播行为有哪些(笔试常问)
      • 事务传播行为进阶知识
        • 前置铺垫,代码示例编写
        • TransactionDefinition.PROPAGATION_REQUIRED
        • TransactionDefinition.PROPAGATIONREQUIRESNEW
        • TransactionDefinition.PROPAGATION_NESTED
        • 事务的几个回滚规则
        • 详解@Transactional 注解
        • Spring AOP自调用问题
        • 事务场景注意事项
      • 小结
      • 参考
    • 来聊聊事务监听
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 深入剖析源码速通Spring多数据源问题
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于sharding-jdbc拓展点实现复杂分库分表算法
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
    • 一文快速掌握高性能内存队列Disruptor
    • 安利一个轻量级流程引擎compileflow
    • 来聊聊一个轻量级的有限状态机Cola-StateMachine
  • Java核心技术
  • 深入理解Spring框架
sharkchili
2022-12-15
目录

探索 Spring 事务的奥秘

# 写在文章开头

在当今的软件开发领域,事务管理是确保数据完整性和一致性的关键环节。而 Spring 事务,作为一个强大且广泛应用的事务管理机制,在构建可靠、高效的应用程序中发挥着至关重要的作用。

当我们踏上对 Spring 事务的探索之旅,就仿佛打开了一扇通往复杂而又精妙世界的大门。在这里,我们将见证事务如何在幕后默默工作,协调各种操作,保障数据的准确性和稳定性。它如同一位无声的守护者,精心呵护着系统的运行。

无论是应对复杂的业务逻辑还是处理大规模的数据交互,Spring 事务都展现出了其非凡的能力和灵活性。通过深入剖析它的原理、特性和应用场景,我们能够更好地理解如何充分发挥其优势,解决实际开发中面临的诸多挑战。让我们一同开启这场精彩的旅程,去揭开 Spring 事务那神秘的面纱,探寻其中蕴含的无尽智慧与可能。

Hi,我是 sharkChili ,是个不断在硬核技术上作死的技术人,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili 。

同时也非常欢迎你star我的开源项目mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)

因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

# 详解Spring中的事务

# 什么是事务

事务在逻辑上可以认为就是把一组操作看作一个动作。这个动作的内容要么都成功,要么都失败,这样才能保证结果的准确性、一致性。

如下代码所示,如果下面这段代码两个插入操作不属于同一个事务的话,结束时只有张三被插入和李四没有插入,不符合业务上的准确性。

使用事务进行数据库增删改查操作时,必须保证当前使用的数据库引擎支持事务,以MySQL为例,MySQL默认引擎为innodb,他就是支持事务的。若时myisam则不支持事务,无法实现数据回滚。

public void transaction_exception_nested_nested(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addNested(user1);
        //报错
		 throw new RuntimeException();
		 
        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addNested(user2);
       
    }
1
2
3
4
5
6
7
8
9
10
11
12

# 事务的特性ACID简介

英文翻译成中文大致是:原子性、隔离性、一致性、持久性。分别代表的含义是:

  1. 原子性(Atomicity): 属于同一个事务的操作要么都成功,要么都失败,直接回滚,数据库的数据像是没有被动过一样。
  2. 一致性(Consistency):在事务开始前和事务结束后,数据库的完整性没有被破坏,即操作符合数据库级联回滚、预设约束、触发器要求。
  3. 隔离性(Isolation)数据库允许并发操作,使用准确的隔离性原则才能保证数据一致性,而隔离级别有:读未提交(Read uncommitted)、读已提交(read committed)、可重复读(repeatable read)、串行化(Serializable)。
  4. 持久性(Durability):持久化的数据不会丢失,即使系统发生故障。

# Spring支持的两种事务管理

有两种姿势,分别是手动式事务和注解式事务,前者是手动的,比较少使用,对应的类是TransactionTemplate或者TransactionManager,使用示例如下所示:

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

或者下面这样一段代码,都是通过都是传入需要进行事务管理的bean定义,进行手动操作管理

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

而后者就比较常用了,基于注解(底层是通过AOP实现的),使用的示例代码如下所示

@Transactional
    public void transaction_exception_nested_nested(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }

1
2
3
4
5
6
7
8
9
10
11
12

# Spring事务隔离级别和传播行为有哪些(笔试常问)

先来说说事务隔离级别:

  1. default(默认):PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别,除了default 其它几个Spring事务隔离级别与JDBC事务隔离级别相对应。
  2. read_uncommited(读未提交):一个事务可以读取另外一个事务未提交的数据,这可能出现脏读 而且不可重复度,出现幻像读等。
  3. read_commited(读已提交):一个事务可以读取另一个事务已经提交的数据,不可以读取未提交的数据 可以避免脏读 但是无法避免不可重复读和幻像读。
  4. repeatTable_read(可重复读):一个事务可以读取另外一个事务已经提交的数据,可以避免脏读的前提下 ,也可以避免不可重复读,但是还是无法避免幻像读。
  5. serializable(串行化):这是一个花费较高但是比较可靠的事务隔离级别,可以避免脏读 幻像读和不可重复读(事务被处理为顺序执行)

Spring事务传播属性:

  1. required(默认属性):Propagation.REQUIRED内外部属于统一事务,一个回滚全部回滚。(后文会有代码演示)
  2. Mandatory:如果当前存在事务,则支持当前事务,如果不存在事务,则抛出异常
  3. Never:以非事务方式执行,如果当前存在事务,则抛出异常
  4. Supports:如果当前存在事务,则支持当前事务,.如果不存在事务,以非事务方式执行
  5. Not_Supports:以非事务方式执行操作,如果存在事务,则挂起当前事务
  6. required_new:在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
  7. Nested:嵌套,支持当前事务,内层事务的执行失败不会导致外层事务的回滚,但是外层事务的回滚会影响内层事务导致内层事务随外层事务一同回滚.

# 事务传播行为进阶知识

# 前置铺垫,代码示例编写

为了更好的解答后续的问题,这里我们给出了一个示例,user1表的service:

@Service
public class User1Service {

    @Resource
    private User1Mapper user1Mapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(User1 user){
        user1Mapper.insert(user);
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(User1 user){
        user1Mapper.insert(user);
    }



    @Transactional(propagation = Propagation.NESTED)
    public void addNested(User1 user){
        user1Mapper.insert(user);
    }



}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

user2表的service,可以看到对于数据库的操作都在注解上标出不同的传播行为:

@Service
public class User2Service {

    @Resource
    private User2Mapper user2Mapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(User2 user){
        user2Mapper.insert(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequiredException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(User2 user){
        user2Mapper.insert(user);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNewException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }


    @Transactional(propagation = Propagation.NESTED)
    public void addNested(User2 user){
        user2Mapper.insert(user);
    }

    @Transactional(propagation = Propagation.NESTED)
    public void addNestedException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# TransactionDefinition.PROPAGATION_REQUIRED

它是Spring的默认传播行为,说白了发生嵌套在内部的事务会和外部的事务融合,所以外部事务报错了内部事务也会回滚。

如下面这段代码,外部的方法没有加事务,且user1Service、user2Service的方法都是PROPAGATION_REQUIRED这个传播级别,所以外部报错不影响两者的内部提交

/**
     * 彼此都有独立的事务,外部没有开事务,所以两者数据都会入库
     */
    @GetMapping("/test/add1")
    public void notransaction_exception_required_required() {
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.addRequired(user2);

        throw new RuntimeException();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

然后我们再来看看这样一段代码,外部没有加事务,所以内部两个事务彼此独立。可以看到user2Service报错,所以只有user1Service插入成功:

 @GetMapping("/test/add2")
    public void notransaction_required_required_exception() {
    	//插入成功
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

		//事务是独立的插入失败
        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.addRequiredException(user2);

        throw new RuntimeException();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

最后在看看这个,外部加了事务,也是REQUIRED,所以内部两个事务与其融合成为一个事务,当外部方法报错,两者插入操作都失败,数据直接回滚:

/**
     * 外部开启事务,报错均回滚
     */
    @GetMapping("/test/add3")
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_exception_required_required() {
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.addRequired(user2);


        throw new RuntimeException();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

再来看看一个比较好玩的,内外部都有事务,第2个内部事务报错,由于三者事务融为一体,所以user2Service的错误被外部transaction_required_required_exception方法感知,user1Service插入也是失败的,所以这个方法两张表都没有插入数据

 @GetMapping("/test/add4")
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_required_exception() {
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        //错误被外部感知,所以所有user1的插入也被回滚了
        user2Service.addRequiredException(user2);
    }
1
2
3
4
5
6
7
8
9
10
11
12

这个也比较特殊,由于三个事务合为一体,所以即使user2Service报错不被感知,两张表的数据也还是没有插入:

@GetMapping("/test/add5")
    @Transactional
    public void transaction_required_required_exception_try() {
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        try {
        //虽然异常被捕获,但是三个内外部事务融合了,一个报错就全部插入回滚
            user2Service.addRequiredException(user2);
        } catch (Exception e) {
            System.out.println("方法回滚");
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

总结:Propagation.REQUIRED内外部属于统一事务,一个回滚全部回滚,无视try块代码的捕获。

# TransactionDefinition.PROPAGATION_REQUIRES_NEW

我们还是通过看代码的方式来讲述吧:第一个例子,外部没有加事务,两个service彼此事务独立,外部报错,但是两者事务都已提交,所以都插入了:

 @GetMapping("/test/add6")
    public void notransaction_exception_requiresNew_requiresNew() {
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addRequiresNew(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.addRequiresNew(user2);
        throw new RuntimeException();

    }
1
2
3
4
5
6
7
8
9
10
11
12

外部还是没有开启事务,user2Service报错事务回滚,所以只有user1Service插入了。

@GetMapping("/test/add7")
    public void notransaction_requiresNew_requiresNew_exception() {
        User1 user1 = new User1();
        user1.setName("张三");
        //正常插入
        user1Service.addRequiresNew(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        //保存回滚了 
        user2Service.addRequiresNewException(user2);
    }
1
2
3
4
5
6
7
8
9
10
11
12

来看一个综合的,外部加了REQUIRED,所以内部第一个事务和外部融合,后两个事务独立,在外部报错的情况下只有addRequired回滚。李四、王五均被插入:

@GetMapping("/test/add8")
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_exception_required_requiresNew_requiresNew() {
        User1 user1 = new User1();
        user1.setName("张三");
        //和外部事务融合,外部报错插入被回滚
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        // 事务独立,不受外部影响,正常插入
        user2Service.addRequiresNew(user2);

        User2 user3 = new User2();
        user3.setName("王五");
        // 事务独立,不受外部影响,正常插入
        user2Service.addRequiresNew(user3);
        throw new RuntimeException();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

外部加了事务,由于王五报错被外部感知,张三的事务和外部融合,所以张三没有被插入,这题只有李四被插入了:

	@GetMapping("/test/add9")
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception() {
        User1 user1 = new User1();
        user1.setName("张三");
        //和外部融合
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        //事务独立,正常插入
        user2Service.addRequiresNew(user2);

        User2 user3 = new User2();
        user3.setName("王五");
        //报错,插入被回滚,外部感知到了错误,所以张三的插入也被回滚了
        user2Service.addRequiresNewException(user3);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

王五报错回滚,但是错误没有被外部感知到,张三和外部事务融合,正常插入、李四正常插入。

@GetMapping("/test/add10")
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception_try() {
        User1 user1 = new User1();
        user1.setName("张三");
        //正常插入
        user1Service.addRequired(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        //和外部事务彼此独立,正常插入
        user2Service.addRequiresNew(user2);
        User2 user3 = new User2();
        user3.setName("王五");
        try {
        // 报错回滚,但错误并没有被外部感知,所以只有这个事务被回滚
            user2Service.addRequiresNewException(user3);
        } catch (Exception e) {
            System.out.println("回滚");
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

总结: 在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。

# TransactionDefinition.PROPAGATION_NESTED

代码如下,外部没有事务,张三、李四彼此独立一个事务,数据均插入,外部异常不影响成功提交:

 @GetMapping("/test/add11")
    public void notransaction_exception_nested_nested() {
        User1 user1 = new User1();
        user1.setName("张三");
        user1Service.addNested(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }
1
2
3
4
5
6
7
8
9
10
11

同理,外部没有事务,后者报错不影响前者正常插入:

 @GetMapping("/test/add12")
    public void notransaction_nested_nested_exception() {
        User1 user1 = new User1();
        user1.setName("张三");
        //独立的事务,不受下方报错影响
        user1Service.addNested(user1);

        User2 user2 = new User2();
        user2.setName("李四");
        //外部没有事务,报错回滚
        user2Service.addNestedException(user2);
    }
1
2
3
4
5
6
7
8
9
10
11
12

外部开启事务(默认级别),内部事务与其融合,一错全部回滚:

 @GetMapping("/test/add13")
    @Transactional
    public void transaction_exception_nested_nested(){
    //外部开启事务,所有nest的事务都与外部事务融合,一个报错全部回滚
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

同上,外部开启事务后内部事务与外部融合,异常能被感知后回滚了:

@GetMapping("/test/add14")
    @Transactional
    public void transaction_nested_nested_exception(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addNestedException(user2);
    }
1
2
3
4
5
6
7
8
9
10
11

异常捕获李四的报错自己消化了,外部不回滚,这就是transaction_nested和REQUIRED的区别:

 @GetMapping("/test/add15")
    @Transactional
    public void transaction_nested_nested_exception_try(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四");
        try {
            user2Service.addNestedException(user2);
        } catch (Exception e) {
            System.out.println("方法回滚");
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

总结:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

# 事务的几个回滚规则

这个我们从源码的注释中就能看出端倪了,如下所示,注释中已经说明了只有运行时异常或者Error可以触发回滚,对于检查型异常是不会回滚。

/**

	 * <p>By default, a transaction will be rolling back on {@link RuntimeException}
	 * and {@link Error} but not on checked exceptions (business exceptions). See
	 * org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
	 */
	Class<? extends Throwable>[] rollbackFor() default {};
1
2
3
4
5
6
7

那我如果要自定义一个异常使用怎么办?如下所示即可,在注解上声明我们需要回滚的错误类型即可。

@Transactional(rollbackFor= MyException.class)

1
2

# 详解@Transactional 注解

@Transactional 作用在不同的地方会有不同的效果,我们最常见的用法就是作用于方法上,如果在方法上加该注解,就会将当前方法中的数据库操作加入事务中。注意方法必须是public否则事务不会生效。 如果作用于类上,则意味着这个类中所有public的方法都会用到事务。接口同理,但我们不建议这么用。

它的常见参数配置如下:

  1. 传播属性(propagation):事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过
  2. 隔离级别(isolation):事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过
  3. 回滚规则(rollbackFor):用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。
  4. 只读属性(readOnly):指定事务是否为只读事务,默认值为 false。
  5. 超时时间(timeout):事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。

# Spring AOP自调用问题

这个问题,我们不妨举个例子来说吧,首先我们看看下面这段代码,很明显如果我们直接调用add17报错了事务会回滚,原因很简单,这个method1加了注解,所以如果我们通过api等工具调用method1时,真正执行这段代码的对象是结果Spring容器bpp处理过的cglib代理类:

	@Transactional
    @GetMapping("/test/add17")
    public void method1(){
        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.insert(user2);
        System.out.println(1/0);
    }
1
2
3
4
5
6
7
8

好了,有了上文的铺垫,我们再来说说嵌套调用失效问题,代码如下所示,当我们使用接口调用工具调用时,发现method1执行出错,李四还是被成功插入了,这是为什么呢? 原因很简单method1执行者并不是cglib代理对象,下面这段method1,完整的代码应该是this.method1,

 @GetMapping("/test/add16")
    public void add16() {
        method1();

    }

    @Transactional
    @GetMapping("/test/add17")
    public void method1(){
        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.insert(user2);
        System.out.println(1/0);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这就导致执行调用method1调用者是this,而不是cglib代理的增强类,如下图所示,正是因为调用者不是代理,导致代理根本不知道method1被调用了,所以事务就失效了

如何解决spring自调用问题呢?

最干脆就是调用时避免嵌套使用就好了,但是有时候应该这个方法要依赖外部的处理逻辑,而外部方法又臭又长改造两量很大导致无法重构。这时候我们只能想别的办法。我以前解决的办法就比较干脆了,既然问题的根源是调用对象错误,那我就干脆找出这个对象来调用不就解决了?

所以我们的思路是这样的,如下代码所示:

首先的controller中假如应用上下文

  @Autowired
    private ApplicationContext applicationContext;
1
2

用这个上下文去容器中把他捞出来调用method1,问题解决

@GetMapping("/test/add16")
    public void add16() {
        TestController t = (TestController) applicationContext.getBean("testController");
        t.method1();


    }

    @Transactional
    @GetMapping("/test/add17")
    public void method1() {
        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.insert(user2);
        System.out.println(1 / 0);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

当然这里还有一种方法,将代理的service类注入,因为spring注入的类都是经过cglib增强的类,所以使用注入的bean也能解决问题,只不过写法很丑陋而已。

@Autowired
private        TestController t;

@GetMapping("/test/add16")
    public void add16() {

        t.method1();


    }

    @Transactional
    @GetMapping("/test/add17")
    public void method1() {
        User2 user2 = new User2();
        user2.setName("李四");
        user2Service.insert(user2);
        System.out.println(1 / 0);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 事务场景注意事项

整体大概有以下几点:

  1. 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败;
  2. 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效
  3. @Transactional 注解的方法所在的类必须被 Spring 管理,否则不生效;
  4. @Transactional 注解只有作用到 public 方法上事务才生效;
  5. 底层使用的数据库必须支持事务机制,否则不生效;

# 小结

自此我们Spring事务和设计理念和底层源码实现并结合相应案例对此进行了深入的分析,希望对你有帮助。

我是 sharkchili ,CSDN Java 领域博客专家,mini-redis的作者,我想写一些有意思的东西,希望对你有帮助,如果你想实时收到我写的硬核的文章也欢迎你关注我的公众号: 写代码的SharkChili 。

同时也非常欢迎你star我的开源项目mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)

因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

# 参考

Spring 事务详解:https://javaguide.cn/system-design/framework/spring/spring-transaction.html#事务属性详解 (opens new window)

详解-脏读、幻读与不可重复读:https://cloud.tencent.com/developer/article/1849815 (opens new window)

透过现象看原理:详解Spring中Bean的this调用导致AOP失效的原因:https://juejin.cn/post/6844903607049912327 (opens new window)

Spring事务:Spring事务隔离级别有几种?分别是什么?Spring事务传播属性有几种?都是什么?:https://blog.csdn.net/LwinnerG/article/details/108708236) (opens new window)

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
深入解读 Spring MVC:Web 开发的得力助手
来聊聊事务监听

← 深入解读 Spring MVC:Web 开发的得力助手 来聊聊事务监听→

最近更新
01
基于EasyExcel实现高效导出
03-25
02
从开源框架中学习那些实用的位运算技巧
03-25
03
浅谈分布式架构设计思想和常见优化手段
03-25
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×