Spring事务(1)

目录

一、事务回顾

1、什么是事务?

2、为什么需要事务?

3、事务的操作

二、Spring 中事务的实现

1、代码准备:

(1)创建项目 spring-trans,引入 Spring Web,MyBatis,MySQL等依赖

(2)配置文件

(3)实体类

(4)Mapper

(5)Service

(6)Controller

2、Spring编程式事务(了解)

(1)观察事务提交

(2)观察事务回滚

3、Spring 声明式事务 @Transactional

(1)添加依赖

(2)在需要事务的方法上添加 @Transactional 注解

(3)Transactional 作用

1、重新抛出异常

2、手动回滚事务


一、事务回顾

        事务是在数据库阶段学习的内容,也是数据库里的机制。

1、什么是事务?

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

        事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求。所以这组操作要么同时成功,要么同时失败。

2、为什么需要事务?

        我们在进行程序开发时,也会有事务的需求。

        比如转账操作:

第一步:A 账户 - 100元。    第二步:B 账户 + 100元。

        如果没有事务,第一步执行成功了,第二步执行失败了,那么 A账户的100元就平白无故消失了。如果使用事务就可以解决这个问题,让这个组操作要么一起成功,要么一起失败。

        比如秒杀系统:

第一步:下单成功。     第二步:扣减库存。

        下单成功后,库存也需要同步减少。如果下单成功,库存扣减失败,那么就会造成下单超出的情况。所以就需要把这两部操作放在同一个事务中。要么一起成功,要么一起失败。

        这里理解事务概念为主,实际企业开发时,并不是简单的通过事务来处理

3、事务的操作

        事务的操作主要有三步:

1、开启事务 start transaction / begin(一组操作前开启事务)

2、提交事务:commit(这组操作全部成功,提交事务)

3、回滚事务:rollback(这组操作中间任何一个操作出现异常,回滚事务)

-- 开启事务
start transaction;-- 提交事务
commit;-- 回滚事务
rollback;

二、Spring 中事务的实现

        Spring 对事务也进行了实现,Spring 中的事务操作分为两类:

1、编程式事务(手动写代码操作事务)。

2、声明式事务(利用注解自动开启和提交事务)。

        在学习事务之前,我们先准备数据和数据的访问代码

需求:用户注册,注册时在日志表中插入一条操作记录。

-- 创建数据库
DROP DATABASE IF EXISTS trans_test;CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;use trans_test;-- 用户表
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (`id` INT NOT NULL AUTO_INCREMENT,`user_name` VARCHAR (128) NOT NULL,`password` VARCHAR (128) NOT NULL,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '用户表';-- 操作日志表
DROP TABLE IF EXISTS log_info;
CREATE TABLE log_info (`id` INT PRIMARY KEY auto_increment,`user_name` VARCHAR ( 128 ) NOT NULL,`op` VARCHAR ( 256 ) NOT NULL,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now()
) DEFAULT charset 'utf8mb4';

1、代码准备

(1)创建项目 spring-trans,引入 Spring Web,MyBatis,MySQL等依赖

(2)配置文件

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/trans_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration: # 配置打印 MyBatis⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true #配置驼峰⾃动转换

(3)实体类

import lombok.Data;
import java.util.Date;@Data
public class UserInfo {private Integer id;private String userName;private String password;private Date createTime;private Date updateTime;
}
import lombok.Data;
import java.util.Date;@Data
public class LogInfo {private Integer id;private String userName;private String op;private Date createTime;private Date updateTime;
}

(4)Mapper

        UserInfoMapper

@Mapper
public interface UserInfoMapper {@Insert("insert into user_info(user_name, password) values (#{name}, #{password})")Integer insert(String name, String password);
}

        LogInfoMapper

@Mapper
public interface LogInfoMapper {@Insert("insert into log_info(user_name, op) values (#{name}, #{op})")Integer insertLog(String name, String op);
}

(5)Service

        UserService:

@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public Integer registry(String name, String password) {return userInfoMapper.insert(name, password);}
}

        LogService:

@Service
public class LogService {@Autowiredprivate LogInfoMapper logInfoMapper;public void insertLog(String name, String op) {//记录用户操作logInfoMapper.insertLog(name, "用户注册");}
}

(6)Controller

@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/registry")public Boolean registry(String name, String password) {//用户注册Integer ret = userService.registry(name, password);return true;}
}

2、Spring编程式事务(了解)

        Spring 手动操作事务和上面 MYSQL操作事务类似,有3个重要操作步骤:

1、开启事务(获取事务)

2、提交事务

3、回滚事务

        SpringBoot 内置了两个对象:

1、DataSourceTransactionManager 事务管理器。用来获取事务(开启事务),提交或回滚事务。

2、TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去,从而获得一个事务 TransactionStatus。

        下面根据代码的实现来学习:

@RequestMapping("/user")
@RestController
public class UserController {//JDBC 事务管理器@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;//定义事务属性@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserService userService;@RequestMapping("/registry")public Boolean registry(String name, String password) {//开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);//用户注册Integer ret = userService.registry(name, password);//提交事务dataSourceTransactionManager.commit(transactionStatus);//回滚事务//dataSourceTransactionManager.rollback(transactionStatus);return true;}
}

(1)观察事务提交

        //提交事务dataSourceTransactionManager.commit(transactionStatus);

        运行程序:127.0.0.1:8080/user/registry?name="lisi"&&password="123"

(2)观察事务回滚

        //回滚事务dataSourceTransactionManager.rollback(transactionStatus);

        运行程序:127.0.0.1:8080/user/registry?name="zhangsan"&&password="1234"

        数据库没多出数据:

        和事务提交相比,日志少了一行。

        以上代码虽然可以实现事务,但操作也很繁琐,有没有更简单的实现方法呢?接下来我们学习声明式事务(注解)。

3、Spring 声明式事务 @Transactional

        声明式事务的实现很简单,两步操作:

(1)添加依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId>
</dependency>

(2)在需要事务的方法上添加 @Transactional 注解

        无需手动开启事务和提交事务,进入方法时自动开启事务。方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务。

        我们来看代码的实现:

@RequestMapping("/trans")
@RestController
public class TransactionalController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) {//用户注册Integer ret = userService.registry(name, password);if(ret > 0) return  "注册成功";return "注册失败";}
}

        运行程序:127.0.0.1:8080/trans/registry?name="wangwu"&&password="12345",发现数据插入成功。

现在修改程序,使之出现异常:

@RequestMapping("/trans")
@RestController
public class TransactionalController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) {//用户注册Integer ret = userService.registry(name, password);//强制程序抛出异常int a = 10/0;if(ret > 0) return  "注册成功";return "注册失败";}
}

        运行程序:127.0.0.1:8080/trans/registry?name="wangwu"&&password="12345",发现数据插入失败。事务发生回滚了。

        上面因为有异常,事务回滚了。

        我们一般会在业务逻辑层当中来控制事务,因为在业务逻辑层当中,一个业务功能可能会包含多个数据访问的操作。在业务逻辑层来控制事务,我们就可以将多个数据访问操作控制在一个事务范围内。上述代码在Controller中书写,只是为了方便学习。

(3)Transactional 作用

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

1、修饰方法时:只有修饰 public 方法时才生效(修饰其他方法时不会报错,也不生效)。[推荐]

2、修饰类时:对 @Transactional 修饰的类中所有的 public 方法都生效。

        方法 / 类被 @Transactional 注解修饰时,在目标方法执行开始前,会自动开启事务,方法执行结束之后,自动提价事务。

        如果在方法执行过程中,出现异常,且异常未被捕获,就进行事务回滚操作

        如果异常被程序捕获,方法就被认为是成功执行,依然会提交事务

修改上述代码,对异常进行捕获:

@RequestMapping("/trans")
@RestController
public class TransactionalController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) {//用户注册Integer ret = userService.registry(name, password);//捕获异常try {int a = 10/0;} catch (Exception e) {e.printStackTrace();}if(ret > 0) return  "注册成功";return "注册失败";}
}

        运行程序:127.0.0.1:8080/trans/registry?name="zhaoliu"&&password="12345",数据库新增了一行数据

        说明事务没有回滚,而是提交事务了。

        上面运行程序,发现虽然程序出错了,但是由于异常被捕获了,所以事务依然得到了提交。如果需要事务进行回滚,有以下两种方式:

1、重新抛出异常

2、手动回滚事务

1、重新抛出异常
@RequestMapping("/trans")
@RestController
public class TransactionalController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) {//用户注册Integer ret = userService.registry(name, password);//强制程序抛出异常try {int a = 10/0;} catch (Exception e) {
//            e.printStackTrace();//将异常抛出去throw e;}if(ret > 0) return  "注册成功";return "注册失败";}
}

        运行程序:127.0.0.1:8080/trans/registry?name="zhaoliu"&&password="12345",发现

        手动抛出异常后,说明事务回滚了。(异常并没有被捕获)

2、手动回滚事务

        使用 TransactionAspectSupport.currentTransactionStatus() 得到当前事务,并使用 SetRollbackOnly 设置 setRollbackOnly。

@RequestMapping("/trans")
@RestController
public class TransactionalController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) {//用户注册Integer ret = userService.registry(name, password);//强制程序抛出异常try {int a = 10/0;} catch (Exception e) {
//            e.printStackTrace();//将异常抛出去
//            throw e;//手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}if(ret > 0) return  "注册成功";return "注册失败";}
}

        运行程序:127.0.0.1:8080/trans/registry?name="zhaoliu"&&password="12345",发现事务回滚了。

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

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

相关文章

【C++】C++前言

目录 一.什么是C 1.1.面向过程&#xff1a; 1.2.面向对象&#xff1a; 二.C发展历史 三.C版本更迭 3.1.语法更新 3.2.关于C2X最新特性的讨论&#xff1a; 3.3.关于C23的一个小故事&#xff1a; 四.C参考文档&#xff1a; 五.C的重要性&#xff1a; 5.1.编程语言排行榜…

JESD204B学习与仿真

平台&#xff1a;vivado2018.3 芯片&#xff1a;xcku115-flva1517-2-i 场景&#xff1a;在高速ADC和DAC芯片中&#xff0c;有使用源同步的时钟和数据同步传输的方式&#xff0c;但是需要在逻辑内部对其进行校准。如果使用jesd204b接口传输数据&#xff0c;设计人员不需要了解…

Vuex看这一篇就够了

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

Win11 改造

记录一些安装 win11 系统之后&#xff0c;对使用不习惯的地方&#xff0c;进行的个人改造 右键菜单 Hiyoung006/Win11Useable: 将Win11右键菜单及资源管理器恢复为Win10样式的脚本 切换到旧版右键菜单&#xff1a; reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34…

Web前端:HTML篇(二)元素属性

HTML 属性 属性是 HTML 元素提供的附加信息。 HTML 元素可以设置属性属性可以在元素中添加附加信息属性一般描述于开始标签属性总是以名称/值对的形式出现&#xff0c;比如&#xff1a;name"value"。 属性实例 HTML 链接由 <a> 标签定义。链接的地址在 href …

数据结构(二叉树-2)

文章目录 一、 实现链式结构二叉树 1.1 Tree.h 1.2 Tree.c 前中后序遍历 前序遍历 中序遍历 后续遍历 1.2 Tree.c 结点个数 1.3Tree.c 叶子节点个数 1.4 Tree.c 二叉树的高度 1.5 Tree.c 层序遍历 1.6 判断是否为完全二叉树 1.7 销毁二叉树 test.c 一、 实现链式结构二叉树 ⽤链…

体重电子秤方案pcba方案设计开发应用

体重电子秤是一种利用电子技术实现物体重量测量的设备。它是现代生活中不可缺少的日常用品之一。本文将从产品介绍、结构设计、工作原理、功能设计和特点优势五个方面来介绍体重电子秤方案产品。 首先&#xff0c;体重电子秤方案的产品介绍。 体重电子秤是一种便携式的设备&…

vue this.$refs 动态拼接

业务需要&#xff0c;refs是不固定的 <vxe-grid refgridWarehouse v-bind"gridWarehouseOptions" v-if"tableHeight" :height"tableHeight":expand-config"{iconOpen: vxe-icon-square-minus, iconClose: vxe-icon-square-plus}"c…

SpringSecurity通用权限管理系统

1、介绍 权限管理是所有后台系统都会涉及的一个重要组成部分&#xff0c;而权限管理的核心流程是相似的&#xff0c;如果每个后台单独开发一套权限管理系统&#xff0c;就是重复造轮子&#xff0c;是人力的极大浪费&#xff0c;本项目就是针对这个问题&#xff0c;提供了一套通…

Python第三方库Kornia中LoFTR的使用

0&#xff0c;背景 浏览LoFTR代码主页&#xff1a;LoFTR&#xff0c;看到其中提到&#xff0c;LoFTR从0.5.11版本开始集成到kornias库中&#xff0c;所以决定尝试。 硬件&#xff1a;联想拯救者Y7000P 2020&#xff0c;i7-10750H&#xff0c;RTX2060 1&#xff0c;Kornia K…

系统变量设置失败导致 /usr/bin:/bin 不在 PATH 环境变量,许多基本命令都无法调用!!手欠的!!!

GPT害人不浅&#xff0c;专坑我这种电脑小白&#xff0c;差点被骗重装系统。。 看起来你的 PATH 环境变量真的缺失了标准的二进制文件路径&#xff08;如 /usr/bin 和 /bin&#xff09;&#xff0c;这导致你无法使用许多常用命令&#xff0c;包括 nano 和 vim。我们需要采取另…

在 CentOS 7 上安装 Docker 并安装和部署 .NET Core 3.1

1. 安装 Docker 步骤 1.1&#xff1a;更新包索引并安装依赖包 先安装yum的扩展&#xff0c;yum-utils提供了一些额外的工具&#xff0c;这些工具可以执行比基本yum命令更复杂的任务 sudo yum install -y yum-utils sudo yum update -y #更新系统上已安装的所有软件包到最新…

使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用

使用 Socket、动态代理、反射 实现一个简易的 RPC 调用 我们前面有一篇 socket 的文章&#xff0c;再之前&#xff0c;还有一篇 java动态代理的文章&#xff0c;本文用到了那两篇文章中的知识点&#xff0c;需要的话可以回顾一下。 下面正文开始&#xff1a; 我们的背景是一个…

【Linux网络】epoll模型构建Reactor_Tcp服务器{协议/客户端/bind/智能指针}

文章目录 1.std::enable_shared_from_this<TcpServer>2.std::bind3.std::make_shared4.std::shared_ptrstd::shared_ptr 和 std::weak_ptr配合使用 5.剖析代码6.整体代码Calculator.hppClientCal.ccCMakeLists.txtCommon.hppEpoller.hppLog.hppMain.ccnocopy.hppProtocol…

YOLOv8预测时报错ValueError

【问题描述】执行YOLOv8预测代码时&#xff1a; # 导入训练好的权重文件做预测 from ultralytics import YOLO# Load a pretrained YOLOv8n model model YOLO("/data/yolov8/runs/detect/train6/weights/best.pt")# Run inference on bus.jpg with arguments model…

四大引用——强软弱虚

目录 一、强引用 二、软引用 三、弱引用 四、虚引用 一、强引用 强引用是在程序代码之中普遍存在的&#xff0c;类似于“Object obj new Object()”&#xff0c;obj变量引用Object这个对象&#xff0c;就叫做强引用。当内存空间不足&#xff0c;Java虚拟机宁愿抛出OutOfMe…

使用 Redis 实现验证码、token 的存储,用自定义拦截器完成用户认证、并使用双重拦截器解决 token 刷新的问题

基于session实现登录流程 1.发送验证码 用户在提交手机号后&#xff0c;会校验手机号是否合法&#xff0c;如果不合法&#xff0c;则要求用户重新输入手机号 如果手机号合法&#xff0c;后台此时生成对应的验证码&#xff0c;同时将验证码进行保存&#xff0c;然后再通过短信…

安防视频监控EasyCVR视频汇聚平台修改配置后无法启动的原因排查与解决

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构&#xff0c;兼容性强、支持多协议接入&#xff0c;包括国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SD…

Linux学习第55天:Linux 4G 通信实验(更快、更高、更强)

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 无论是有线网络还是WiFi都是摆脱不了布线的尴尬&#xff0c;而4G通信可以彻底拜托网线的束缚&#xff0c;实现无线网络通信。 而说到4G就不得不提到5G&#xff0c;中…

关于css中flex布局垂直居中失效问题的原因

项目中遇到用flex进行页面布局后&#xff0c;使用上下居中设置&#xff1a;align-item: center; 目标效果如下&#xff1a; 但是失效&#xff0c;不起作用&#xff0c;如下图所示&#xff1a; 各种排查过后发现设置了子模块 align-self 属性&#xff0c;这会覆盖容器上的 al…