Spring事务

一.事务的概念及应用

1.1什么是事务

事务的定义:事务是⼀组操作的集合, 是⼀个不可分割的操作.
事务会把所有的操作作为⼀个整体, ⼀起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成功, 要么同时失败.
事务的特性(ACID)

Spring Boot中的事务同样遵循ACID特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability):

  1. 原子性:事务中的所有操作作为一个整体执行,它们要么全部成功,要么全部失败回滚。
  2. 一致性:事务执行前后,数据库必须从一个一致性状态转变到另一个一致性状态。
  3. 隔离性:并发执行的事务之间应该相互隔离,避免数据冲突和不一致性。
  4. 持久性:一旦事务提交,其对数据库的修改就是永久性的,即使在系统故障后也能够恢复。

1.2为什么需要事务

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

假设你正在使用网上银行进行转账操作,你想从你的储蓄账户中转出1000元到你的信用卡账户以偿还欠款。这个转账操作实际上包含了两个步骤:1)从储蓄账户中扣除1000元;2)向信用卡账户中存入1000元。

如果银行系统没有使用事务来管理这两个步骤,那么可能会出现以下问题:

  1. 部分执行:如果第一个步骤(从储蓄账户中扣款)成功执行了,但是第二个步骤(向信用卡账户存款)因为某种原因(如系统故障)未能执行,那么你的储蓄账户就会减少1000元,而信用卡账户却没有增加相应的金额,导致数据不一致。

  2. 重复执行:如果两个步骤都成功执行了,但是因为系统错误,银行系统误以为转账没有成功,并尝试再次执行这两个步骤,那么你的储蓄账户就会被错误地扣除两次1000元,而信用卡账户也会增加两次1000元,这同样会导致数据不一致。

为了避免这些问题,银行系统会使用事务来管理这两个步骤。事务会确保这两个步骤要么都成功执行,要么都失败回滚,从而保持数据的一致性和完整性。如果第一个步骤成功执行了,但是第二个步骤失败了,那么事务会回滚第一个步骤,将储蓄账户中的1000元返还给你,确保你的账户余额不会受到错误操作的影响。

⽐如秒杀系统,
第⼀步: 下单成功
第⼆步: 扣减库存
下单成功后, 库存也需要同步减少. 如果下单成功, 库存扣减失败, 那么就会造成下单超出的情况. 所以就
需要把这两步操作放在同⼀个事务中. 要么⼀起成功, 要么⼀起失败

1.3 事务的操作

1.事务的开始

  1. 显式事务:在需要显式控制事务的情况下,可以通过特定的命令或API来开始一个事务。例如,在SQL中,可以使用BEGIN TRANSACTIONSTART TRANSACTION命令来标记事务的开始。在Spring框架中,可以通过在方法上添加@Transactional注解来声明一个事务的边界。

  2. 隐式事务和自动事务:在某些数据库或框架中,事务的开始可能是隐式的或自动的。例如,在某些数据库管理系统中,每条SQL语句都被视为一个自动提交的事务,除非显式地开始了一个更大的事务。在Spring框架中,也可以通过配置来启用声明式事务管理,使得事务的创建、提交和回滚都自动进行。

2.事务的执行

在事务开始之后,就可以执行一系列的数据库操作了。这些操作可以是查询、插入、更新或删除等。在事务的执行过程中,所有的操作都被视为一个整体,它们要么全部成功,要么全部失败。

3.事务的提交

如果事务中的所有操作都成功执行了,那么就需要提交事务。提交事务的操作会将事务中所有的更改永久地保存到数据库中。一旦事务被提交,就不能再对其进行回滚操作了。

在SQL中,可以使用COMMIT命令来提交事务。在Spring框架中,当声明了@Transactional注解的方法正常结束时(即没有抛出未检查异常),事务管理器会自动提交事务。

简而言之: 事务的操作主要有三步:
1. 开启事start transaction/ begin (⼀组操作前开启事务)
2. 提交事务: commit (这组操作全部成功, 提交事务)
3. 回滚事务: rollback (这组操作中间任何⼀个操作出现异常, 回滚事务)
-- 开启事务
start transaction;
-- 提交事务
commit;
-- 回滚事务
rollback;

 二.Spring 中事务的实现

Spring 中的事务操作分为两类:
1. 编程式事务(⼿动写代码操作事务).
2. 声明式事务(利⽤注解⾃动开启和提交事务)
在学习事务之前, 我们先准备数据和数据的访问代码
需求: ⽤⼾注册, 注册时在⽇志表中插⼊⼀条操作记录.
SQL数据准备:
-- 创建数据库
DROP DATABASE IF EXISTS trans_test;
CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;
-- ⽤⼾表
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. 创建项⽬ spring-trans, 引⼊Spring Web, Mybatis, mysql等依赖
2. 配置⽂件
spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/trans_test?
characterEncoding=utf8&useSSL=false
 username: root
 password: root
 driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
 configuration: # 配置打印 MyBatis⽇志
 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 map-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;
}
4. Mapper
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserInfoMapper {
 @Insert("insert into user_info(`user_name`,`password`)values(#{name},#
{password})")
 Integer insert(String name,String password);
}
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LogInfoMapper {
 @Insert("insert into log_info(`user_name`,`op`)values(#{name},#{op})")
 Integer insertLog(String name,String op);
}
5. Service
@Slf4j
@Service
public class UserService {
 @Autowired
 private UserInfoMapper userInfoMapper;
 public void registryUser(String name,String password){
 //插⼊⽤⼾信息
 userInfoMapper.insert(name,password);
 }
}
@Slf4j
@Service
public class LogService {
 @Autowired
 private LogInfoMapper logInfoMapper;
 public void insertLog(String name,String op){
 //记录⽤⼾操作
 logInfoMapper.insertLog(name,"⽤⼾注册");
 }
}
6.Contrller
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
 @Autowired
 private UserService userService;
 @RequestMapping("/registry")
 public String registry(String name,String password){
 //⽤⼾注册
 userService.registryUser(name,password);
 return "注册成功";
  }
}

  • 37
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值