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

Spring事务和事务传播机制

目录

  • 一.事务
    • 1.1 介绍
    • 1.2 事务的操作
  • 二.Spring事务
    • 2.1 Spring编程式事务(了解)
    • 2.2 Spring声明式事务 @Transactional
  • 三.@Transactional详解
    • 3.1 异常回滚属性 rollbackFor
    • 3.2 MySQL事务隔离级别
    • 3.3 Spring事务隔离级别 isolation
    • 3.4 事务传播机制 propagation
    • 3.5 NESTED和REQUIRED的区别

一.事务

1.1 介绍

事务是一组操作的集合,是一个不可分割的操作,事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求,最终这组操作要么同时成功,要么同时失败

1.2 事务的操作

事务的操作主要分为:

  • 开启事务:在一组操作开始前开启事务
  • 提交事务:这组操作全部成功,则提交事务
  • 回滚事务:这组操作中任何一个操作出现异常,则回滚事务
// 开启事务
start transaction;
//提交事务
commit;
//回滚事务
rollback;

二.Spring事务

Spring也对事务进行了实现,其中事务操作分为两类:编程式事务声明式事务。编程式事务通过手动编写代码来进行事务操作,而声明式事务利用注解自动开启和提交事务

2.1 Spring编程式事务(了解)

Spring内置了两个对象:

  • DataSourceTransactionManager:事务管理器,用去获取事务,提交事务或回滚事务
  • TransactionDefinition:事务的属性,在获取事务时传递给事务管理器
import lombok.extern.slf4j.Slf4j;
import org.example.transdemo.service.UserService;
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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    //JDBC事务管理器
    @Autowired
    private DataSourceTransactionManager transactionManager;

    //定义事务属性
    @Autowired
    private TransactionDefinition transactionDefinition;
    
    @RequestMapping("/register")
    public Boolean register(String name,String password) {
        // 开启事务
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        Integer result=userService.register(name,password);
        log.info("插入操作影响行数为:{}", result);
        // 回滚事务
        transactionManager.rollback(transaction);
        // 提交事务
        transactionManager.commit(transaction);
        return true;
    }
}

2.2 Spring声明式事务 @Transactional

Spring声明式事务即在需要事务的方法上添加@Transactional注解,进入方法时自动开启事务,方法执行完自动提交事务,如果方法在执行过程中出现异常,且异常未被捕获,则回滚事务;如果异常被程序捕获,则提交事务。@Transaction注解即可以修饰方法也可以修饰类,当修饰方法时,方法需要使用public修饰,否则不生效;当修饰类时,对类中所有public方法都生效

import lombok.extern.slf4j.Slf4j;
import org.example.transdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/user")
@Transactional
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/register")
    public Boolean register(String name,String password) {
        try{
            Integer result = userService.register(name,password);
            log.info("插入操作影响行数为:{}", result);
            return true;
        } catch (Exception e){
            // e.printStackTrace(); 捕获异常,提交事务
            // throw e; 抛出异常,回滚事务
          	// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 回滚事务
            return false;
        }
    }
}

三.@Transactional详解

@Transaction注解中含有三个常见属性:

  • rollbackFor:异常回滚属性,指定能够触发事务回滚的异常类型,可以指定多个异常类型
  • isolation:事务的隔离级别,默认值为Isolation.DEFAULT
  • propagation:事务的传播几种,默认值为Propagation.REQUIRED

3.1 异常回滚属性 rollbackFor

@Transactional默认只在遇到运行时异常(RuntimeException及其子类)Error时才会回滚,非运行时异常不进行回滚,如果需要指定事务回滚的异常类型,就需要通过rollbackFor属性来配置

import lombok.extern.slf4j.Slf4j;
import org.example.transdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/user")
// 对于所有异常的抛出都进行回滚
@Transactional(rollbackFor = Exception.class)
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/register")
    public Boolean register(String name,String password) {
        try{
            Integer result = userService.register(name,password);
            log.info("插入操作影响行数为:{}", result);
            return true;
        } catch (Exception e){
            // e.printStackTrace(); 捕获异常,提交事务
            // throw e; 抛出异常,回滚事务
            return false;
        }
    }
}

3.2 MySQL事务隔离级别

在并发场景下,MySQL事务会一些数据问题:

  • 脏读:A事务正在修改数据但未提交,此时B事务去读取这条数据,这时A事务回滚,B事务读取的就是脏数据
  • 幻读:B事务前后两次读取同一个范围的数据,在B事务两次读取的过程中A事务新增了数据,导致B事务后一次读取到前一次查询没有看到的行
  • 不可重复读:B事务读取了两次数据,在这两次读取过程中A事务修改了数据,导致B事务这两次读取出来的数据不一致

了解完这些数据问题后,我们接着来介绍MySQL的事务隔离级别。SQL标准定义了四种隔离级别:

  • 读未提交(READ UNCOMMITED):读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据
  • 读提交(READ COMMITED):读已提交,也叫提交读,该隔离级别的事务能读到已经提交事务的数据
  • 可重复读(REPEATABLE READ):MySQL的默认事务隔离级别,事务不会读到其他事务对已有数据的修改,即便其他事务已经提交,这样确保了同一事物多次查询结果一致,但是其他事务插入新数据是可以感知到的
  • 串行化(SERIALIZABLE):序列化,事务最高隔离级别,强制事务排序使得冲突不会发生,从而解决脏读,幻读和不可重复读问题,但执行效率较低
事务隔离级别脏读不可重复读幻读
读未提交(READ UNCOMMITED)
读提交(READ COMMITED)×
可重复读(REPEATABLE READ)××
串行化(SERIALIZABLE)×××

3.3 Spring事务隔离级别 isolation

Spring中事务隔离级别包含五种:

  1. Isolation.DEFAULT:以连接数据库的事务隔离级别为主
  2. Isolation.READ_UNCOMMITED:读未提交,对应SQL标准中的读未提交
  3. Isolation.READ_COMMITED:读已提交,对应SQL标准中的读已提交
  4. Isolation.REPEATABLE_READ:可重复读,对应SQL标准的可重复读
  5. Isolation.SERIALIZABLE:串行化,对应SQL标准的串行化

3.4 事务传播机制 propagation

事务的传播机制指的是当多个事务方法存在调用关系时,事务是如何在这些方法之间进行传播的,Spring的事务传播机制有七种,以下结合类比的场景(一对新人要结婚,是否需要房子)进行介绍:

  • Propagation.REQUIRED:默认的事务传播级别,如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新事务(需要有房子,如果你有房就一起住;如果没有房就一起买房)
  • Propagation.SUPPORTS:如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行(可以有房子,如果有房就一起住,如果没房就租房)
  • Propagation.MANDATORY:强制性,如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常(必须有房子,如果没房就不结婚)
  • Propagation.REQUIRES_NEW:创建一个新事务,如果当前存在事务,则把当前事务挂起,即不管外部方法是否开启事务,都会新增开启自己的事务,且开启的事务相互独立,互不干扰(必须买新房,不管有没有房子,必须要一起买新房,即使有房也不住)
  • Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起(不需要房,不管有没有房都不住,必须租房)
  • Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常(不能有房子)
  • Propagation.NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果没有事务,则等价于Propagation.REQUIRED(如果没房就一起买房,如果有房就把房作为根据地来做点生意)

3.5 NESTED和REQUIRED的区别

  1. 整个事务如果全部执行成功,二者结果相同
  2. 如果事务一部分执行成功,REQUIRED加入事务会导致整个事务回滚,NESTED嵌套事务可以实现局部回滚,不会影响上一个方法中执行的结果
  • 使用NESTED
//UserController类
import lombok.extern.slf4j.Slf4j;
import org.example.transdemo.service.LogService;
import org.example.transdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/user")
@Transactional
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    @RequestMapping("/register")
    public Boolean register(String name,String password) {
        Integer result = userService.register(name,password);
        log.info("用户表插入操作影响行数为:{}", result);
        Integer logCount = logService.insertLog(name,"register");
        log.info("日志表插入操作影响行数为:{}", result);
        return true;
    }
}

//UserService类
import org.example.transdemo.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 UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer register(String name,String password) {
        return userInfoMapper.insert(name,password);
    }
}

//LogService类
import org.example.transdemo.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 LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertLog(String name,String op){
        Integer result = logInfoMapper.insertLog(name,op);
        try{
            int num = 10/0;
        } catch (Exception e){
            // 回滚当前事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }
}

在这里插入图片描述
使用NESTED可以让嵌套事务部分回滚,即仅回滚insertLog方法,用户表插入数据成功,日志表插入失败

  • 使用REQUIRED
//只需要修改insertLog方法的事务传播机制,其他类保持不变
import org.example.transdemo.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 LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertLog(String name,String op){
        Integer result = logInfoMapper.insertLog(name,op);
        try{
            int num = 10/0;
        } catch (Exception e){
            // 回滚当前事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }
}

在这里插入图片描述
使用REQUIRED会致整个事务回滚,用户表和日志表都插入失败

相关文章:

  • 浏览器自动携带cookie注意事项
  • 12:表的内外连接
  • LVGL学习1
  • 数据库练习2
  • 图像回归评价的常用指标
  • 个人学习编程(3-22) leetcode刷题
  • C++智能指针详解
  • 蓝桥杯备赛 背包问题
  • [项目]基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050配置与读取
  • 建筑安全员考试:“实战演练” 关键词助力的答题提升策略
  • Sqlite3数据库
  • 客服机器人怎么才能精准的回答用户问题?
  • Linux shell脚本-概述、语法定义、自定义变量、环境变量、预设变量、变量的特殊用法(转义字符、单双引号、大小括号)的验证
  • Labview和C#调用KNX API 相关东西
  • 《深度剖析:鸿蒙系统不同终端设备的UI自适应布局策略》
  • Linux系统管理与编程06:任务驱动综合应用
  • APIJSON快速入门
  • 西门子仿真实例位置
  • Linux小知识
  • SCI论文阅读指令(特征工程)
  • 央行回应美债波动:单一市场、单一资产变动对我国外储影响总体有限
  • 人民日报头版:上海纵深推进浦东高水平改革开放
  • 清华成立人工智能医院,将构建“AI+医疗+教育+科研”闭环
  • 申花四连胜领跑中超,下轮榜首大战对蓉城将是硬仗考验
  • 乌方称泽连斯基与特朗普进行简短会谈
  • 年客流超2500万,九岁的上海国际旅游度假区有哪些文旅商体实践?