17. Spring 事务

news2024/11/16 9:22:55

目录

1. 事务定义

2. MySQL 中的事务使用

3. 没有事务时的插入

4. Spring 编程式事务

5. Spring 声明式事务

5.1  @Transactional 作用范围

5.2  @Transactional 参数说明

5.3  @Transactional 工作原理


1. 事务定义

将⼀组操作封装成一个执行单元(封装到一起),要么全部成功,要么全部失败。
为什么要用事务?
比如转账分为两个操作:
第一步操作:A -100
第二步操作:A+100
果没有事务,第一步执行成功了,第二步执行失败了,那么 A 账户平高白无故的 100 元就没有 了。而如果使用事务就可以解决这个问题,让这⼀组操作要么一起成功,要么一起失败。

2. MySQL 中的事务使用

事务在 MySQL 有 3 个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:
--开启事务
start transaction;
--业务执行

--提交事务
commit;

--回滚事务
rollback;

3. 没有事务时的插入

@Mapper
public interface UserMapper {
    // 插入数据
    Integer insert(User user);
    
}
@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private Date createtime;
    private Date updatetime;

    public User(){

    }
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <insert id="insert">
        insert into userinfo (username,password,photo)values(#{username},#{password},#{photo})
</mapper>
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public Integer insert(User user){
        return userMapper.insert(user);
    }
}
@RequestMapping("/trans")
@RestController
public class TransactionalController {
    @Autowired
    private UserService userService;
    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        User user = new User(username,password);
        return userService.insert(user);
    }
}

 在运行前查看数据库的数据如下图所示:

 运行以上代码后可以看到:

此时可以看到数据成功插入: 

4. Spring 编程式事务

@Slf4j
@RequestMapping("/trans")
@RestController
public class TransactionalController {
    @Autowired
    private UserService userService;
    // 获取数据库事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    private TransactionDefinition transactionDefinition;
    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:" +result);
        // 事务回滚
        dataSourceTransactionManager.rollback(transaction);
        return result;
    }
}

运行成功,返回1: 

 但是,此时可以看到数据库中的数据并未添加:

这是因为事务进行了回滚。接下来我们看一下事务的提交

@Slf4j
@RequestMapping("/trans")
@RestController
public class TransactionalController {
    @Autowired
    private UserService userService;
    // 获取数据库事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:" +result);
        // 事务回滚
//        dataSourceTransactionManager.rollback(transaction);
        // 提交事务
        dataSourceTransactionManager.commit(transaction);
        return result;
    }
}

可以看到数据此时提交成功:

5. Spring 声明式事务

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;

    @Transactional
    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        return result;
    }
}

 根据打印的日志我们可以看到数据提交成功了:

那么,我们如何让事务进行回滚呢?

手动添加异常:

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;

    @Transactional
    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        int a = 10/0;
        return result;
    }
}

 运行结果:

 可以看到数据并没有提交:

 查看日志可以发现,此时打印的日志和事务回滚时的日志相同:

此时,我们去掉注解 @Transactional:

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;

//    @Transactional
    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        int a = 10/0;
        return result;
    }
}

也就是说,在没有注解 @Transactional 时,数据是可以提交成功的;添加注解 @Transactional,当有异常时,事务会进行回滚。

通过注解,不需要我们手动开启事务和关闭事务,如果程序执行成功,自动提交事务;如果程序执行异常,自动回滚事务。

5.1  @Transactional 作用范围

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

  • 修饰方法:只能应用到 public 方法上,否则不生效
  • 修饰类:表明该注解对该类中所有的 public 方法都生效

5.2  @Transactional 参数说明

接下来,我们设置在发生算数异常时,不进行回滚:

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;

    @Transactional(noRollbackFor = ArithmeticException.class)
    @RequestMapping("/addUser")
    public Integer addUser(String username,String password){
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        int a = 10/0;
        return result;
    }
}

 接下来我们手动扔出异常:

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;

    /**
     * 指定异常回滚
     * @param username
     * @param password
     * @return
     */
    @Transactional
    @RequestMapping("/addUser2")
    public Integer addUser2(String username,String password) throws Exception {
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        throwException();
        return result;
    }
    public void throwException() throws Exception{
        throw new IOException();
    }
}

 可以看到并没有进行回滚。

@Transactional 默认只在遇到运行时异常和Error时才会回滚,非运行时异常不回滚,即 Exception 的子类中,除了 RuntimeException 及其子类。

显式的指定所有异常均需回滚:

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;
    /**
     * 指定异常回滚
     * @param username
     * @param password
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    // 显式的指定所有异常均需要回滚
    @RequestMapping("/addUser2")
    public Integer addUser2(String username,String password) throws Exception {
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        throwException();
        return result;
    }
    public void throwException() throws Exception{
        throw new IOException();
    }
}

可以看到事务进行了回滚:

 如果异常被捕获,事务不会回滚:

@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
    @Autowired
    private UserService userService;

    @Transactional
    @RequestMapping("/addUser3")
    public Integer addUser3(String username,String password) throws Exception {
        User user = new User(username,password);
        Integer result = userService.insert(user);
        log.info("影响行数:"+result);
        try{
            int a = 10/0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }
}

可以看到事务没有回滚,正常提交: 

5.3  @Transactional 工作原理

@Transactional 是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。
@Transactional 在开始执行业务之前,通过代理先开启事务,在执行成功之后再提交事务。如果中途到异常,则回滚事务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/832206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LBP特征笔记

LBP&#xff0c;局部二值模式&#xff08;Local Binary Pattern&#xff09;&#xff0c;是一种描述图像局部纹理特征的方式&#xff0c;具有旋转不变性和灰度不变性。首先由T. Ojala, M.Pietikinen, 和 D. Harwood 在1994年提出。 LBP特征描述 基础LBP算子 基础的LBP算子定义…

什么是耗尽型MOS管

我们常用的MOS管属于增强型MOS管&#xff0c;对于N沟道增强型MOS管&#xff0c;需要UGS大于开启电压&#xff0c;MOS管才能导通。 其实还有一种MOS管&#xff0c;当UGS大于0&#xff0c;等于0&#xff0c;小于0时MOS管都能导通&#xff0c;这种MOS管叫做耗尽型 MOS管。 以N沟道…

复现原型链污染漏洞

目录 一、复现原型链污染漏洞 hackit 2018 1、创建hackit_2018.js文件 2、运行hackit_2018.js文件 3、寻找原型链漏洞 4、污染原型链 hackit 2018 1、创建hackit_2018.js文件 const express require(express) var hbs require(hbs); var bodyParser require(body-par…

485modbus转profinet网关连接6台ABB 变频器通讯案例

昆仑通态通过485Modbus转Profinet网关实现了1200PLC与6台ABB变频器之间的Modbus通讯&#xff0c;触摸屏本地实时监控。485Modbus转Profinet网关作为中间桥梁&#xff0c;实现1200PLC与ABB变频器之间的互联互通&#xff0c;使得数据的传输变得高效可靠。 打开博图添加PLC,在这里…

2.4G芯片XL2408开发板,SOP16封装,芯片集成1T 8051内核单片机

XL2408开发板可用于2.4G芯片XL2408开发板的开发调试。XL2408烧录仿真需要使用WS_LINK。XL2408开发板烧录仿真需要接4根线&#xff1a;PA13:DIO&#xff0c;PA14:CLK&#xff0c;VCC&#xff0c;GND。 XL2408芯片集成射频收发机、频率收生器、晶体振荡器、调制解调器等功能模块,…

GY-NEO6MV2 GPS模块测试

GY-NEO6MV2 GPS模块测试 &#x1f4d3;模块资料&#xff1a;GY-NEO6MV2 https://pan.baidu.com/s/17ihqLzXytrR2K2LmumvdKA&#x1f341;原理图&#xff08;资料中有&#xff09; &#x1f4d1;基本参数信息 &#x1f33f;3V-5V供电通用。&#x1f33f;默认波特率&#xff…

【JAVASE】static成员

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; static 1. static修饰成员变量2. static…

公司来了个拿 25K 出来的测试,算是见识到了基础的天花板

今天上班开早会就是新人见面仪式&#xff0c;听说来了个很厉害的大佬&#xff0c;年纪还不大&#xff0c;是上家公司离职过来的&#xff0c;薪资已经达到中高等水平&#xff0c;很多人都好奇不已&#xff0c;能拿到这个薪资应该人不简单&#xff0c;果然&#xff0c;自我介绍的…

数学知识(一)

一、数论 1.1质数 定义:在所有大于1的自然数&#xff0c;如果只包含1和本身这两个约数&#xff0c;就被称为质数(素数). 质数的判断:试除法 bool is_prime(int n) {if(n < 2) return false;for(int i 2;i < n / i;i ){if(n % i 0)return false;}return true; } 分…

一文学会git常用命令和使用指南

文章目录 0. 前言1.分支分类和管理1. 分支分类规范&#xff1a;2. 最佳实践3. 分支命名规范示例&#xff1a;4. 分支管理方法&#xff1a; 2. commit 注释规范1. 提交注释结构&#xff1a;2. 提交注释的准则&#xff1a; 3. git 常用命令1. git pull 核心用法2. git push 命令1…

C++初阶 - 6.模板初阶

目录 1.泛型编程 2.函数模板 2.1函数模板的概念 2.2函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5模板参数的匹配原则 3.类模板 3.1类模板的定义格式 3.2类模板的实例化 1.泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int& left…

SpringMVC概述、SpringMVC的工作流程、创建SpringMVC的项目

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 Spring MVC入门 一、Spring MVC概述二、入门案例2.1导入Sp…

【宝藏系列】Linux 常用磁盘管理命令详解

【宝藏系列】Linux 常用磁盘管理命令详解 文章目录 【宝藏系列】Linux 常用磁盘管理命令详解前言1️⃣ df2️⃣du3️⃣fdisk&#x1f4df;磁盘格式化&#x1f4e0;磁盘检验⌨️磁盘挂载与卸除&#x1f4c0;卸载/dev/hdc6 前言 Linux磁盘管理常用三个命令为df、du和fdisk。 df…

Java框架(九)--Spring Boot入门(1)

SpringBoot 2.x入门简介 学前基础 Maven Spring MVC理念 开发环境 Spring Boot官网版本介绍 https://spring.io/projects/spring-boot#learn 我们点击 Reference Doc. &#xff0c;再点击Getting Started&#xff0c;就可以看到官网系统环境说明了 官网系统环境说明 Sp…

Nginx安装和Nginx配置虚拟主机

Nginx安装 源码包获取地址&#xff1a;http://nginx.org/download/ RPM包获取地址&#xff1a;http://nginx.org/packages/centos/7Server/x86_64/RPMS/ RPM安装 这里选择的RPM包是 nginx-1.22.0-1.el7.ngx.x86_64.rpm [rootlocalhost ~]# yum install nginx-1.22.0-1.el7.…

RabbitMQ:概念和安装,简单模式,工作,发布确认,交换机,死信队列,延迟队列,发布确认高级,其它知识,集群

1. 消息队列 1.0 课程介绍 1.1.MQ 的相关概念 1.1.1.什么是MQ MQ(message queue&#xff1a;消息队列)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是message 而已&#xff0c;还是一种跨进程的通信机制…

k8s集群部署nacos,采用的是 emptyDir 临时目录挂载

官方参考地址&#xff1a;https://nacos.io/zh-cn/docs/use-nacos-with-kubernetes.html 说明&#xff1a; 1、官网采用的nfs持久化部署 我将nacos持久化改成 emptyDir 临时目录挂载&#xff0c;同时又能满足自行调节nacos集群实例数。 2. emptyDir 临时目录挂载的nacos.ya…

记录 Vue3 + Ts 类型使用

阅读时长: 10 分钟 本文内容&#xff1a;记录在 Vue3 中使用 ts 时的各种写法. 类型大小写 vue3 ts 项目中&#xff0c;类型一会儿大写一会儿小写。 怎么区分与基础类型使用? String、string、Number、number、Boolean、boolean … 在 js 中&#xff0c; 以 string 与 String…

TS协议之PAT(节目关联表)

1. 概要 PAT&#xff1a;节目关联表&#xff0c;与PMT成对出现&#xff0c;包含所有的频道编号&#xff1b;是解析ts数据的起点。 PAT数据结构如下&#xff1a; PAT数据结构 字段分析&#xff1a; TS头&#xff1a;参考TS协议之PES&#xff08;数据包&#xff09;&#xf…

Cpp学习——模板

模板&#xff1f; 目录 模板&#xff1f; 1.介绍 2.函数模板的使用 3.函数模板的强制转换or显式调用 四,模板的分类 1.介绍 在Cpp3.0中&#xff0c;祖师爷便引入了模板的概念。这是一个重大的变革&#xff0c;为后来的Cpp标准化打下了铺垫。也正是因为有了模板&#xff0…