【Java EE进阶 --- SpringBoot】Spring事务

🚀 欢迎来到我的CSDN博客:Optimistic _ chen

一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!


🚀我的专栏推荐

专栏 内容特色 适合人群
🔥C语言从入门到精通 系统讲解基础语法、指针、内存管理、项目实战 零基础新手、考研党、复习
🔥Java基础语法 系统解释了基础语法、类与对象、继承 Java初学者
🔥Java核心技术 面向对象、集合框架、多线程、网络编程、新特性解析 有一定语法基础的开发者
🔥Java EE 进阶实战 Servlet、JSP、SpringBoot、MyBatis、项目案例拆解 想快速入门Java Web开发的同学
🔥Java数据结构与算法 图解数据结构、LeetCode刷题解析、大厂面试算法题 面试备战、算法爱好者、计算机专业学生

🚀我的承诺:

✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例

✅ 持续更新:专栏内容定期更新,紧跟技术趋势

✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)


🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨

📌 标签:#技术博客#编程学习#Java#C语言#算法#程序员

文章目录

前言

事务是⼀组操作的集合,是⼀个不可分割的操作.

事务这个词,在数据库学习后都会有了解,如它的概念:事务会把所有的操作作为一个整体,向数据库发起请求,要么都成功,要么一起失败。

事务的基本操作:

  1. 开启事务 :start transaction/begin(一组操作前开启事务)
  2. 提交事务:commit (操作全部成功,提交事务)
  3. 回滚事务:rollback(一组操作中任何一个操作出现问题,整组回滚)

Spring 中的事务

Spring 中的事务分为两类:

  • 编程式事务(手动写代码操作事务)
  • 声明式事务(利用注解自动开启和提交事务)

准备工作

数据库:

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';

同时引入Spring Web, Mybatis , mysql 等多种依赖
设置配置文件

java 复制代码
spring:
 datasource:
   url: jdbc:mysql://127.0.0.1:3306/trans_testcharacterEncoding=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 #配置驼峰⾃动转换

实体类:

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

Controller:

java 复制代码
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/registry")
    public String registry(String name,String password){
        //用户注册(数据操作)
        userService.registryUser(name,password);
        log.info("用户注册成功");
        return "注册成功";
    }
}

Service:

java 复制代码
@Service
@Slf4j
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    public Integer registryUser(String name,String password){
         return userInfoMapper.insert(name,password);
    }
}
java 复制代码
@Service
@Slf4j
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;
    public Integer insertLog(String name,String password){
       return  logInfoMapper.insertLog(name,"用户注册");
    }
}

Mapper:

java 复制代码
@Mapper
public interface UserInfoMapper {
    @Insert("insert into user_info(`user_name`,`password`) value(#{name},#{password})")
    Integer insert(String name,String password);
}
java 复制代码
@Mapper
public interface LogInfoMapper {
    @Insert("insert into log_info(`user_name`,`op`)values(#{name},#{op})")
    Integer insertLog(String name,String op);
}

Spring编程式事务

Spring手动操作事务与MySQL类似,三个关键步骤:开启事务、提交事务、回滚事务。

Spring Boot内置的两个对象:

  • DataSourceTransactionManager 事务管理器:用来获取事务(开启事务),提交或者回滚事务。
  • TransactionDefinition 是事务的属性,获取事务时将属性传进去从而获得一个事务的TransactionStatus
java 复制代码
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;//事务管理器

    @Autowired
    private TransactionDefinition definition;//事务之前状态

    @RequestMapping("/registry")
    public String registry(String name,String password){
        /**
         * 1. 开启事务
         * 2. 数据操作
         * 3. 事务提交、回滚
         */
        //开启事务
        TransactionStatus transaction= dataSourceTransactionManager.getTransaction(definition);
        //用户注册(数据操作)
        userService.registryUser(name,password);
        log.info("用户注册成功");
        //事务提交
        //dataSourceTransactionManager.commit(transaction);
        //事务回滚
        dataSourceTransactionManager.rollback(transaction);
        return "注册成功";
    }
}

postman测试:提交事务

Spring声明式事务(@Transactional)

在需要事务的方法上添加@Transactional注解就可以实现,无需手动开启事务,方法执行完会自动提交事务,中途有异常自动回滚事务。

java 复制代码
@Slf4j
@RequestMapping("/user2")
@RestController
public class UserController2 {
    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        userService.registryUser(name,password);
        return "注册成功";
    }
}

运行此程序,发现数据插入成功,但是我们给代码添加异常,会发生什么呢?

java 复制代码
@Slf4j
@RequestMapping("/user2")
@RestController
public class UserController2 {
    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        userService.registryUser(name,password);
        int a=10/0;
        return "注册成功";
    }
}


虽然用户注册成功,但是去观察数据库发现没有新增数据,说明事务进行了回滚。

@Transcational

作用

@Transcational可以修饰方法或类:

  • 修饰方法:只有修饰public方法时才生效
  • 修饰类:对 @Transcational 修饰的类中所有public 方法生效

类\方法被@Transcational修饰后,在目标方法执行前就自动开启事务,方法执行完毕自动提交事务。 注意:如果方法执行中有异常且未捕获,就进行事务回滚;否则继续提交事务。

针对异常被捕获还需要对事务进行回滚,有两种方法解决:

  1. 重新抛出异常
java 复制代码
try {
    //强制程序抛出异
    int a = 10/0;
 }catch (Exception e){
    //将异常重新抛出去
    throw e;
}
  1. 手动回滚
java 复制代码
try {
    //强制程序抛出异
    int a = 10/0;
 }catch (Exception e){
    //手动回滚事务
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    
}

属性

@Transcational注解中常见有三个属性:rollbackFor、Isolation、propagation

rollbackFor

rollbackFor:异常回滚属性。指定能够触发事务回滚类型的异常类型

@Transactional 默认只在遇到运⾏时 异常和Error时才会回滚,⾮运⾏时异常不回滚.

如果需要所有异常都回滚,那配置@Transcational属性中rollbackFor,指定什么异常类型时进行回滚

java 复制代码
@Transactional(rollbackFor = Exception.class)

注意:
默认情况下,只有运行时异常RuntimeExpection和Error时才回滚。但是其他类型异常可以通过rollbackFor属性进行指定。

Isolation

Isolation:事务隔离级别,默认是Isolation.DEFAULT

其实MySQL种有4种事务隔离级别:

  1. 读未提交(READ_UNCOMMITTED):也叫未提交读.该隔离级别的事务可以看到其他事务中未提交的数据.
  2. 读提交(READ_COMMITTED):也叫提交读,该隔离级别的事务能读到已提交事务的数据
  3. 可重复读(REPEATABLE_READ):事务不会读到其他事务对已有数据的修改,即使其他事务已提交。保证同一事务多次查询结果一致(MySQL默认事务隔离级别)
  4. 串行化(SERIALIZABLE):序列化,事务最⾼隔离级别.它会强制事务排序,使之不会发⽣冲突。

而Spring中事务隔离级别有5种:

  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主
  • Isolation.READ_UNCOMMITTED:读未提交
  • Isolation.READ_COMMITTED:读已提交
  • Isolation.REPEATABLE_READ:可重复读
  • Isolation.SERIALIZABLE:串行化

脏读:因为其他事务未提交的数据可能会发⽣回滚,但是该隔离级别却可以读到,我们把该级别读到的数据称之为脏数据。
不可重复读:由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同SQL查询可能会得到不同的结果
幻读:可重复读级别的事务正在执行,另一个事务成功插入某条数据,但是因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因). 明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去

事务隔离级别 脏读 不可重复读 幻读
读未提交
读已提交 ×
可重复读 × ×
串行化 × × ×

完结撒花!🎉

如果这篇博客对你有帮助,不妨点个赞支持一下吧!👍
你的鼓励是我创作的最大动力~

想获取更多干货? 欢迎关注我的专栏 → optimistic_chen

📌 收藏本文,下次需要时不迷路!

我们下期再见!💫 持续更新中......


悄悄说:点击主页有更多精彩内容哦~ 😊

相关推荐
leonardee2 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
序属秋秋秋2 小时前
《Linux系统编程之进程基础》【进程状态】
linux·运维·c语言·c++·笔记·操作系统·进程状态
Slow菜鸟2 小时前
Java 开发环境安装指南(一) | 目录设计规范
java
q***0562 小时前
Spring 中使用Mybatis,超详细
spring·tomcat·mybatis
BS_Li2 小时前
【Linux系统编程】进程控制
java·linux·数据库
多多*2 小时前
分布式中间件 消息队列Rocketmq 详解
java·开发语言·jvm·数据库·mysql·maven·java-rocketmq
因为奋斗超太帅啦2 小时前
Git分布式版本控制工具学习笔记(一)——git本地仓库的基本使用
笔记·git·学习
從南走到北2 小时前
JAVA外卖霸王餐CPS优惠CPS平台自主发布小程序+公众号霸王餐源码
java·开发语言·小程序
李昊哲小课2 小时前
手写 Spring Boot 嵌入式Tomcat项目开发教学
spring boot·后端·tomcat