SpringBoot事务管理详解:从基础到高级实战(一个字:全)
wptr33 2025-06-10 02:11 16 浏览
一、事务管理基础概念
1.1 什么是事务?
专业解释:事务(Transaction)是数据库操作的一个执行单元,它由一个或多个SQL语句组成,这些语句要么全部执行成功,要么全部失败回滚。
通俗理解:就像银行转账,A给B转100元,需要从A账户扣100,同时给B账户加100。这两个操作必须同时成功或同时失败,不能只完成一个。
1.2 事务的四大特性(ACID)
特性 | 全称 | 解释 | 生活化例子 |
原子性 | Atomicity | 事务是不可分割的工作单位,要么全部执行,要么全部不执行 | 要么转账完全成功,要么完全失败,不会出现钱扣了但对方没收到的情况 |
一致性 | Consistency | 事务执行前后,数据库从一个一致状态变到另一个一致状态 | 转账前后,A和B的账户总额保持不变 |
隔离性 | Isolation | 多个事务并发执行时,一个事务的执行不应影响其他事务 | 多人同时给同一个人转账,结果应该和顺序执行一样正确 |
持久性 | Durability | 事务一旦提交,其结果就是永久性的 | 转账成功后,即使系统崩溃,结果也不会丢失 |
1.3 Spring事务管理核心接口
Spring事务管理主要基于以下几个核心接口:
- PlatformTransactionManager - 事务管理器顶层接口
- TransactionDefinition - 事务定义信息(隔离级别、传播行为等)
- TransactionStatus - 事务运行状态
二、SpringBoot事务基础配置
2.1 快速启用事务管理
SpringBoot中只需添加@
EnableTransactionManagement注解即可启用事务管理(实际上SpringBoot自动配置已经帮我们做了)
@SpringBootApplication
@EnableTransactionManagement // 可省略,SpringBoot自动配置会启用
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
2.2 配置数据源和事务管理器
SpringBoot自动配置会为我们创建
DataSourceTransactionManager,我们也可以自定义:
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
// 这里以HikariCP为例
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
三、声明式事务管理
3.1 @Transactional注解基础使用
@Service
public class OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private AccountDao accountDao;
@Transactional
public void createOrder(Order order) {
// 1. 创建订单
orderDao.insert(order);
// 2. 扣减账户余额
accountDao.deduct(order.getUserId(), order.getAmount());
// 如果这里抛出异常,上面两个操作都会回滚
}
}
3.2 @Transactional注解属性详解
属性 | 类型 | 说明 | 默认值 |
propagation | Propagation | 事务传播行为 | REQUIRED |
isolation | Isolation | 事务隔离级别 | DEFAULT(使用数据库默认) |
timeout | int | 事务超时时间(秒) | -1(使用系统默认) |
readOnly | boolean | 是否只读事务 | false |
rollbackFor | Class[] | 指定哪些异常类触发回滚 | {} |
rollbackForClassName | String[] | 指定哪些异常类名触发回滚 | {} |
noRollbackFor | Class[] | 指定哪些异常类不触发回滚 | {} |
noRollbackForClassName | String[] | 指定哪些异常类名不触发回滚 | {} |
3.3 事务传播行为详解
传播行为定义了事务方法相互调用时事务如何传播。
传播行为 | 解释 | 生活化例子 |
REQUIRED | 如果当前没有事务,就新建一个事务;如果已经存在一个事务,就加入这个事务 | 朋友聚餐:如果已经有人组织(有事务)就加入;没人组织就自己组织一个 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 | 朋友聚餐:有人组织就一起;没人组织就各自AA |
MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 | 必须有人组织聚餐,没人组织就生气 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 | 不管有没有人组织,自己单独再组织一次 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 | 不参与集体活动,自己单独行动 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 | 拒绝参加有组织的聚餐 |
NESTED | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与REQUIRED类似的操作 | 大聚餐中的小聚餐,可以单独回滚小聚餐 |
代码示例:
@Service
public class OrderService {
@Autowired
private InventoryService inventoryService;
@Transactional(propagation = Propagation.REQUIRED)
public void placeOrder(Order order) {
// 主业务逻辑
saveOrder(order);
try {
// 调用库存服务,使用REQUIRES_NEW传播行为
inventoryService.updateStock(order.getProductId(), order.getQuantity());
} catch (Exception e) {
// 库存更新失败不影响订单创建
log.error("库存更新失败", e);
}
}
}
@Service
public class InventoryService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateStock(Long productId, int quantity) {
// 更新库存逻辑
}
}
3.4 事务隔离级别详解
隔离级别定义了一个事务可能受其他并发事务影响的程度。
隔离级别 | 解释 | 可能的问题 | 生活化例子 |
DEFAULT | 使用数据库默认隔离级别 | 取决于数据库 | 使用公司默认规定 |
READ_UNCOMMITTED | 允许读取未提交的变更(脏读) | 脏读、不可重复读、幻读 | 看同事未保存的文档草稿 |
READ_COMMITTED | 只能读取已提交的数据 | 不可重复读、幻读 | 只能看同事已保存的文档 |
REPEATABLE_READ | 确保同一事务中多次读取同样数据结果一致 | 幻读 | 拍照记录同事文档状态,之后查看照片 |
SERIALIZABLE | 最高隔离级别,完全串行化执行 | 无 | 锁门独自修改文档,其他人必须等待 |
代码示例:
@Service
public class AccountService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public BigDecimal getAccountBalance(Long accountId) {
// 这个方法保证读取的是已提交的数据
return accountDao.getBalance(accountId);
}
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 转账操作需要可重复读,保证多次读取余额一致
BigDecimal fromBalance = accountDao.getBalance(fromId);
if (fromBalance.compareTo(amount) < 0) {
throw new RuntimeException("余额不足");
}
// 其他操作...
}
}
四、编程式事务管理
除了声明式事务,Spring还提供了编程式事务管理。
4.1 使用TransactionTemplate
@Service
public class OrderService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private OrderDao orderDao;
public void createOrder(final Order order) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
// 业务逻辑
orderDao.insert(order);
// 其他操作...
} catch (Exception e) {
status.setRollbackOnly(); // 标记为回滚
throw e;
}
}
});
}
}
4.2 使用PlatformTransactionManager
@Service
public class OrderService {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private OrderDao orderDao;
public void createOrder(Order order) {
// 定义事务属性
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
definition.setTimeout(30);
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 业务逻辑
orderDao.insert(order);
// 其他操作...
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
五、事务高级特性
5.1 事务回滚规则
默认情况下,事务只在遇到运行时异常(RuntimeException)和错误(Error)时回滚,检查异常(Checked Exception)不会触发回滚。
自定义回滚异常:
@Transactional(rollbackFor = {BusinessException.class, SQLException.class})
public void businessOperation() throws BusinessException {
// 业务逻辑
if (someCondition) {
throw new BusinessException("业务异常");
}
}
5.2 事务超时设置
@Transactional(timeout = 30) // 单位秒
public void longRunningProcess() {
// 这个方法执行时间超过30秒会触发超时回滚
}
5.3 只读事务
@Transactional(readOnly = true)
public BigDecimal getAccountBalance(Long accountId) {
// 只读操作,可以优化性能
return accountDao.getBalance(accountId);
}
六、分布式事务
6.1 JTA与XA事务
对于跨多个数据源或消息队列的分布式事务,可以使用JTA。
配置示例:
@Configuration
@EnableTransactionManagement
public class JtaTransactionConfig {
@Bean
public JtaTransactionManager transactionManager() {
return new JtaTransactionManager();
}
}
6.2 Spring的分布式事务解决方案
- XA协议:传统两阶段提交
- TCC模式:Try-Confirm-Cancel
- SAGA模式:长事务解决方案
- 本地消息表:最终一致性
- Seata:阿里巴巴开源的分布式事务解决方案
七、常见问题与解决方案
7.1 事务失效的常见场景
场景 | 原因 | 解决方案 |
方法非public | Spring AOP限制 | 改为public方法 |
自调用 | 代理失效 | 注入自身或使用AopContext |
异常被捕获 | 异常未传播 | 重新抛出或设置rollbackFor |
数据库不支持 | 如MyISAM引擎 | 使用InnoDB引擎 |
传播行为设置错误 | 如NOT_SUPPORTED | 调整传播行为 |
7.2 自调用问题解决方案
问题代码:
@Service
public class OrderService {
public void createOrder(Order order) {
validateOrder(order);
// 其他逻辑...
}
@Transactional
public void validateOrder(Order order) {
// 验证逻辑
}
}
解决方案1:注入自身
@Service
public class OrderService {
@Autowired
private OrderService self; // 注入自身
public void createOrder(Order order) {
self.validateOrder(order); // 通过代理调用
// 其他逻辑...
}
@Transactional
public void validateOrder(Order order) {
// 验证逻辑
}
}
解决方案2:使用AopContext
@Service
public class OrderService {
public void createOrder(Order order) {
((OrderService) AopContext.currentProxy()).validateOrder(order);
// 其他逻辑...
}
@Transactional
public void validateOrder(Order order) {
// 验证逻辑
}
}
八、性能优化与最佳实践
8.1 事务性能优化建议
- 尽量缩短事务范围:只在必要的地方使用事务
- 合理设置隔离级别:不要过度使用SERIALIZABLE
- 避免大事务:将大事务拆分为多个小事务
- 合理使用只读事务:对查询操作使用readOnly=true
- 注意异常处理:避免不必要的回滚
8.2 事务最佳实践
- 事务注解应放在实现类上:而不是接口
- 明确指定回滚异常:使用rollbackFor明确指定
- 避免在事务中处理耗时操作:如RPC调用、IO操作等
- 合理设置超时时间:避免长时间占用连接
- 注意事务传播行为的选择:根据业务场景选择合适的传播行为
九、完整案例演示
9.1 电商下单完整事务案例
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductDao productDao;
@Autowired
private AccountDao accountDao;
@Autowired
private CouponDao couponDao;
@Autowired
private InventoryService inventoryService;
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = {BusinessException.class, RuntimeException.class})
@Override
public OrderResult createOrder(OrderRequest request) throws BusinessException {
// 1. 验证订单基本信息
validateOrderRequest(request);
// 2. 锁定库存(使用REQUIRES_NEW传播行为,独立事务)
inventoryService.lockInventory(request.getProductId(), request.getQuantity());
// 3. 扣减账户余额
deductAccountBalance(request.getUserId(), request.getTotalAmount());
// 4. 使用优惠券(如果有)
if (request.getCouponId() != null) {
useCoupon(request.getUserId(), request.getCouponId());
}
// 5. 创建订单
Order order = createOrderRecord(request);
// 6. 发送创建订单事件(异步,不影响主事务)
sendOrderCreatedEvent(order);
return convertToResult(order);
}
private void validateOrderRequest(OrderRequest request) throws BusinessException {
// 验证逻辑...
if (request.getQuantity() <= 0) {
throw new BusinessException("购买数量必须大于0");
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void lockInventory(Long productId, int quantity) throws BusinessException {
// 锁定库存逻辑...
}
private void deductAccountBalance(Long userId, BigDecimal amount) throws BusinessException {
// 扣减余额逻辑...
}
@Transactional(propagation = Propagation.MANDATORY)
public void useCoupon(Long userId, Long couponId) throws BusinessException {
// 使用优惠券逻辑...
}
private Order createOrderRecord(OrderRequest request) {
// 创建订单记录逻辑...
}
private void sendOrderCreatedEvent(Order order) {
// 异步发送事件...
}
}
十、事务监控与调试
10.1 日志配置
在application.properties中添加:
# 开启Spring事务日志
logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
10.2 使用TransactionSynchronization
可以在事务的不同阶段执行回调:
@Transactional
public void businessMethod() {
// 业务逻辑...
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
// 事务提交后执行
sendNotification();
}
@Override
public void afterCompletion(int status) {
// 事务完成后执行(status: 0=提交, 1=回滚)
cleanUpResources();
}
});
}
总结
SpringBoot事务管理是开发企业级应用的重要部分。通过本文的全面介绍,你应该已经掌握了:
- 事务的基本概念和ACID特性
- SpringBoot事务的配置和使用方式
- 声明式和编程式事务的实现
- 事务传播行为和隔离级别的详细解析
- 高级特性和常见问题的解决方案
- 分布式事务的基本概念
- 性能优化和最佳实践
关注我?别别别,我怕你笑出腹肌找我赔钱。
头条对markdown的文章显示不太友好,想了解更多的可以关注微信公众号:“Eric的技术杂货库”,后期会有更多的干货以及资料下载。
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
如何将AI助手接入微信(打开ai手机助手)
-
Java面试必考问题:什么是乐观锁与悲观锁
-
redission YYDS spring boot redission 使用
-
SparkSQL——DataFrame的创建与使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)