当前位置: 首页 > news >正文

Spring 事务 - 实践

目录

前言

一、事务

1. 事务的概念

2. Spring 中的事务

1. 编程式事务

2. 声明式事务

二、 @Transactional

1. rollbackFor

2. isolation

3. Spring 事务的传播机制

4. Spring 事务传播机制演示

1. required

2. requires_new

3. nested

4. required 和 nested 的区别


前言

本文介绍了Spring框架中的事务管理机制。主要内容包括:1. 事务基本概念,即一组操作的原子性执行;2. Spring提供的两种事务实现方式:编程式事务(手动管理)和声明式事务(通过@Transactional注解);3. @Transactional注解的核心属性:rollbackFor(异常回滚规则)、isolation(隔离级别)和propagation(传播机制);4. 重点讲解了7种事务传播机制及其应用场景,特别是REQUIRED、REQUIRES_NEW和NESTED的区别。文章通过代码示例展示了不同传播机制下的行为差异,帮助开发者根据业务需求选择合适的事务管理策略。


一、事务

1. 事务的概念

事务是一组操作的集合,是一个不可分割的操作;

事务会把组内的操作视作一个整体,如果所有操作全部成功,事务进行提交,如果有一个或者多个步骤失败,就会将成功的操作也进行回滚,实现整体上全部成功,或者全部不成功的功能;

事务的操作有 3 个步骤:

  • 1. 开启事务;
  • 2. 提交事务;
  • 3. 回滚事务;

2. Spring 中的事务

Spring 中的事务操作分为两类:

  • 编程式事务:手动编程,操作事务;
  • 声明式事务:利用注解,自动开启,提交或者回滚事务;

1. 编程式事务

DataSourceTransactionManager:事务管理器,用来获取事务,开启事务,提交事务,回滚事务;

TransactionDefinition:事务的属性,在获取事务的时候,将 TransactionDefinition 传递进去,从而获得一个事务 TransactionStatus;

手动实现事务的提交和回滚:

package com.example.trans.controller;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/user")
public class UserInfoController {@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserInfoService userInfoService;@RequestMapping("/register")public Boolean register(String userName, String password){// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 3. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);if(result <= 0){return false;}}catch (Exception e){return false;}// 4. 提交事务
//        dataSourceTransactionManager.commit(transactionStatus);// 5. 回滚事务dataSourceTransactionManager.rollback(transactionStatus);return true;}
}

2. 声明式事务

使用  @Transactional 注解,自动实现事务的开启,提交和回滚;

package com.example.trans.controller;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/user2")
public class UserInfoController2 {@Autowiredprivate UserInfoService userInfoService;// @Transactional 自动实现事务的开启,提交或者回滚@Transactional@RequestMapping("/register")public Boolean register(String userName, String password) throws IOException {// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);int n = 10 / 0;if(result <= 0){return false;}}catch (Exception e){// 手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//            return false;}return true;}
}

 @Transactional 可以用来修饰方法和类:

  • 修饰方法时,只有修饰被 public 修饰的方法才生效;
  • 修饰类时,对类中的所有被 public 修饰的方法生效;

事务提交和回滚的情况分析:

当所有操作执行成功,事务提交;

当发生运行时异常或者错误时,且异常没有被捕获,事务会自动回滚;

如果发生运行时异常且异常被捕获,则事务提交;

如果发生的是受查异常,则事务会提交;

如果发生运行时异常后,捕获到了异常,仍然想要实现回滚:可以再次抛出异常,也可以手动回滚;

如果发生的是受查异常,仍然想要回滚,可以设置 @Transactional 的属性 rollbackFor 为 Exception.class 类型;

二、 @Transactional

@Transactional 常见的三个属性:

rollbackFor:异常回滚属性,能够指定多个异常类型,发生指定类型的异常时,事务会实现回滚;

Isolation:事务的隔离级别;

propagation:事务的传播机制;

1. rollbackFor

package com.example.trans.controller;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/user2")
public class UserInfoController2 {@Autowiredprivate UserInfoService userInfoService;// @Transactional 自动实现事务的开启,提交或者回滚@Transactional(rollbackFor = Exception.class)@RequestMapping("/register")public Boolean register(String userName, String password) throws IOException {// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);int n = 10 / 0;if(result <= 0){return false;}}catch (Exception e){throw new IOException();}return true;}
}

设置 rollbackFor 的类型为 Exception.class 后,只要发生异常,就会实现回滚;

2. isolation

MySQL 有 4 种隔离级别,分别是:读未提交,读已提交,可重复度和串行化;

读未提交:事务 A 写数据时,事务 B 可以读到,如果事务 A 之后又修改了数据,事务 B 读到的就是脏数据,也称为脏读;

读已提交:当事务 A 提交后,事务 B 能读到,事务 C 又修改了当初事务 A 提交的数据,此时事务 B 再读,发现数据前后不一致了,称为不可重复读;

可重复读:当事务 A 提交后,事务 B 不能读到事务 A 产生的数据,但是当 事务 B 想要插入数据时,发现数据行的 ID 已经被占用,事务 B 能感知到数据发生变化,但是读不到,称为幻读;

串行化: 把事务进行排序,一个一个执行,完全没有并发;

MySQL 的默认隔离界别时可重复读;Spring 的默认隔离级别是采用数据库设置的隔离级别;

package org.springframework.transaction.annotation;
public enum Isolation {DEFAULT(-1),READ_UNCOMMITTED(1),READ_COMMITTED(2),REPEATABLE_READ(4),SERIALIZABLE(8);private final int value;private Isolation(int value) {this.value = value;}public int value() {return this.value;}
}

3. Spring 事务的传播机制

传播机制:当多个事务方法存在调用关系,事务在这些方法的传播方式;

事务的隔离机制解决的是多个事务同时调用一个数据的问题;

事务的传播机制解决的是一个事务在多个方法中传递的问题;

@Transactional 可以设置 propagation 属性,设置事务的传播机制;

传播机制有 7 种:

package org.springframework.transaction.annotation;
public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);private final int value;private Propagation(int value) {this.value = value;}public int value() {return this.value;}
}

Propagation.REQUIRED:默认事务传播级别,如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务;

Propagation.REQUIRES_NEW:不管当前是否存在事务(如果有事务则挂起),都要创建一个新的事务;

Propagation.NESTED:如果当前存在一个事务,则创建一个新的事务,作为嵌套事务运行,如果嵌套事务发生异常,且异常被 catch 住了,则不影响原有事务,如果当前不存在事务,则创建一个新的事务运行;

Propagation.SUPPORTS:如果当前存在事务,则加入该事务,如果当前不存在事务,则以非事务的方式运行;

Propagation.NOT_SUPPORTED:如果当前存在事务,则挂起,以非事务的方式运行;

Propagation.MANDATORY:如果当前不存在事务,则抛出异常;

Propagation.NEVER:如果当前存在事务,则抛出异常;

4. Spring 事务传播机制演示

1. required

用户注册接口:

package com.example.trans.controller;
import com.example.trans.service.LogInfoService;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/user2")
public class UserInfoController2 {@Autowiredprivate UserInfoService userInfoService;@Autowiredprivate LogInfoService logInfoService;// @Transactional 自动实现事务的开启,提交或者回滚@Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)@RequestMapping("/register")public Boolean register(String userName, String password) throws IOException {// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);Integer result2 = logInfoService.insertLogInfo(userName, "用户注册");if(result <= 0 || result2 <= 0){return false;}}catch (Exception e){// 手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return true;}
}

UserInfoService:

package com.example.trans.service;
import com.example.trans.mapper.UserInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserInfoService {@Autowiredprivate UserInfoMapper userInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer insertUserInfo(String userName, String password) {return userInfoMapper.insertUserInfo(userName, password);}
}

LogInfoService:

package com.example.trans.service;
import com.example.trans.mapper.LogInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LogInfoService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer insertLogInfo(String userName, String op) {return logInfoMapper.insertLogInfo(userName, op);}
}

成功运行时:

LogInfoService 抛出异常:事务不会被提交

package com.example.trans.service;
import com.example.trans.mapper.LogInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LogInfoService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer insertLogInfo(String userName, String op) {int n = 10 / 0;return logInfoMapper.insertLogInfo(userName, op);}
}

2. requires_new

 注册接口代码不变,更改 UserInfoService 和 LogInfoService 的传播机制为 requires_new:

UserInfo 提交成功,LogInfo 没有提交;

3. nested

注册接口代码不变,更改 UserInfoService 和 LogInfoService 的传播机制为 nested;

捕获 LogInfoService 中的异常,并手动回滚事务:

package com.example.trans.service;
import com.example.trans.mapper.LogInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Service
public class LogInfoService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.NESTED)public Integer insertLogInfo(String userName, String op) {try{int n = 10 / 0;}catch(Exception e){// 手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return logInfoMapper.insertLogInfo(userName, op);}
}

测试:事务提交,LogInfo 实现了手动回滚;

4. required 和 nested 的区别

如果事务全部执行成功,二者是相同的;

如果事务一部分执行成功,required 加入事务会使整个事务全部回滚,nested 如果 catch 住异常,能手动实现部分回滚,不影响整个事务的提交;


http://www.jsqmd.com/news/36962/

相关文章:

  • 2025 年 11 月危险品运输厂家推荐排行榜,危险品运输车,危险品运输罐,危险品运输物流,危险品运输公司专业实力与安全服务深度解析
  • Spring AI Alibaba 项目源码学习(二)-Graph 定义与描述分析
  • 20232422 2024-2025-1 《网络与系统攻防技术》实验四实验报告
  • SpringBoot热启动
  • SPI 设备与多从机冲突的解决之道:片选管理、CS 去抖与总线隔离策略 - 实践
  • 2025 年 11 月超声波检测设备厂家推荐排行榜,超声波检测系统,相控阵/高频/水浸/液冷板/钎焊超声波检测,高频相控阵超声波检测设备厂家推荐
  • 对于生成虚tree进行DP——CF1097G Vladislav and a Great Legend
  • 2025 年 11 月除蜡水厂家推荐排行榜,钢铁除蜡水,不锈钢除蜡水,金属除蜡水,工业除蜡水公司推荐
  • 使用napi-rs,通过node调用rust代码
  • 20232309 2025-2026-1 《网络与系统攻防技术》实验四实验报告
  • 智语写作都有哪些功能?看这一篇就够了!智语写作全功能详解
  • pythontip 字符串转为字典
  • Microsoft Activation Scripts (MAS)
  • rufus.ini
  • 团队作业2
  • Explorer++
  • Interpretability-Guided Test-Time Adversarial Defense
  • JavaWeb04-JUnit
  • 2025 年 11 月开窗器厂家推荐排行榜,链条开窗器,机芯开窗器,配件开窗器,电动开窗器公司推荐
  • 详细介绍:用户体验就是新SEO:如何同时提升搜索者满意度和搜索排名
  • P6688 可重集 笔记
  • 哪款学习机适合小学生用?2025年11月多款主流品牌告诉你如何选
  • AIGC系统
  • noip5
  • 20232320 2024-2025-1 《网络与系统攻防技术》实验四实验报告
  • 20232326 2025-2026-1 《网络与系统攻防技术》实验四实验报告
  • #题解#洛谷P3143
  • STM32环境监测架构开发实践
  • [GESP202303 二级] 百鸡问题
  • 2025 年 11 月码垛机厂家推荐排行榜,多样板材码垛机,倒板码垛机,分拣码垛机,上料码垛机,下料码垛机,码垛机械手,全自动码垛机,龙门码垛机公司推荐