【JavaEE】Spring事务

目录

  • 一、事务简介
  • 二、Spring事务的实现
    • [2.1 事务的操作](#2.1 事务的操作)
    • [2.2 分类](#2.2 分类)
      • [2.2.1 Spring编程式事务](#2.2.1 Spring编程式事务)
      • [2.2.2 Spring 声明式事务 @Transactional](#2.2.2 Spring 声明式事务 @Transactional)
        • [2.2.2.1 @Transactional 详解](#2.2.2.1 @Transactional 详解)
          • [2.2.2.1.1 rollbackFor](#2.2.2.1.1 rollbackFor)
          • [2.2.2.1.2 Isolation](#2.2.2.1.2 Isolation)
          • [2.2.2.1.3 propagation](#2.2.2.1.3 propagation)

一、事务简介

事务:事务是⼀组操作的集合,是⼀个不可分割的操作。事务会把所有的操作作为⼀个整体,⼀起操作。所以这组操作要么同时成功,要么同时失败。

二、Spring事务的实现

2.1 事务的操作

事务的操作主要有三步:

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

2.2 分类

Spring 中的事务操作分为两类:

  1. 编程式事务(⼿动写代码操作事务)
  2. 声明式事务(利⽤注解⾃动开启和提交事务)

2.2.1 Spring编程式事务

SpringBoot 内置了两个对象:

  1. DataSourceTransactionManager 事务管理器。⽤来获取事务(开启事务),提交或回滚事务
  2. TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus

示例:

java 复制代码
package com.example.springtransdemo.controller;


import com.example.springtransdemo.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;
@RequestMapping("/user")
@RestController
public class UserController {
      @Autowired
      private UserService userService;
      //事务属性
      @Autowired
      private TransactionDefinition definition;
      //事务管理器
      @Autowired
      private DataSourceTransactionManager transactionManager;
      @RequestMapping("/registry")
      public String registry(String name,String password) {
       //开启事务
       TransactionStatus transaction = transactionManager.getTransaction(definition);
       //⽤⼾注册
       userService.registryUser(name, password);
       //提交事务
       transactionManager.commit(transaction);
       //回滚事务
       //transactionManager.rollback(transaction);
       return "注册成功";
      }
}

当我们不提交时可以在数据库中添加字段,回滚后就不会在数据库中添加字段,提交和回滚方法不能在同一个事务中调用。

2.2.2 Spring 声明式事务 @Transactional

使用这个注解需要添加依赖,但是如果已经添加了MyBatis的依赖,那么就包含了下面需要引入的依赖的内容。

xml 复制代码
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
</dependency>

注解的应用,在需要使用到事务的方法上加上注解,就会自动提交事务状态。当发⽣了没有处理的error和RunTimeException异常(默认情况下)的时候,就会自动回滚。

例如上面的代码就可以改为:

java 复制代码
        @RequestMapping("/registry")
        @Transactional
        public String registry(String name,String password) {
            //⽤⼾注册
            userService.registryUser(name, password);
            return "注册成功";
        }

那么当我们就是要在捕获处理了异常后,还是将事务进行回滚时,我们就可以手动回滚使用TransactionAspectSupport类下使用currentTransactionStatus方法获取状态,使用isRollbackOnly方法回滚。

java 复制代码
 @RequestMapping("/registry")
        @Transactional
        public String registry(String name,String password) {
            //⽤⼾注册
            userService.registryUser(name, password);
            try{
                int a = 10 / 0;
            } catch (Exception e) {
                log.error("错误");
                TransactionAspectSupport.currentTransactionStatus().isRollbackOnly();
            }
            return "注册成功";
        }
2.2.2.1 @Transactional 详解

修饰范围:

  • 修饰类时,就是修饰类中的所用访问修饰限定符为public的方法。
  • 修饰方法时,只能修饰访问修饰限定符为public的方法。

@Transactiona 常见三大属性:

  1. rollbackFor:异常回滚属性。指定能够触发事务回滚的异常类型。可以指定多个异常类型。
  2. Isolation:事务的隔离级别。默认值为 Isolation.DEFAULT。
  3. propagation:事务的传播机制。默认值为 Propagation.REQUIRED 。
2.2.2.1.1 rollbackFor

@Transactional 默认只在遇到运⾏时异常RunTimeException和Error时才会回滚。

例如执行下面的操作的时候,事务不会回滚,数据库中还是会添加数据。

java 复制代码
        @RequestMapping("/registry")
        @Transactional
        public String registry(String name,String password) throws IOException {
            //⽤⼾注册
            userService.registryUser(name, password);
            if(true) {
                throw new IOException();
            }
            return "注册成功";
        }

但是我们加上属性rollbackFor就会在发生rollbackFor指定的异常的时候回滚。

java 复制代码
        @RequestMapping("/registry")
        @Transactional(rollbackFor = Exception.class)
        public String registry(String name,String password) throws IOException {
            //⽤⼾注册
            userService.registryUser(name, password);
            if(true) {
                throw new IOException();
            }
            return "注册成功";
        }

还有与rollbackFor相似作用的属性:

  • rollbackFor :使用class作为值,在发生该异常的时候回滚。
  • rollbackForClassName:使用类名作为值,在发生该异常的时候回滚。
  • noRollbackFor :使用class作为值,在发生该异常的时候不回滚。
  • noRollbackForClassName:使用类名作为值,在发生该异常的时候不回滚。

    当我们同时使用@Transactionsl的noRollbackFor和rollbackFor对同一异常类处理,后使用的会覆盖前面的。例如 @Transactional(rollbackFor = IOException.class, noRollbackFor = IOException.class)生效的是noRollbackFor = IOException.class
2.2.2.1.2 Isolation

Spring事务的隔离级别有以下几种值:

  • DEFAULT:数据库是什么隔离级别,这个方法就还是什么隔离级别。

剩下的都对应MySQL的隔离级别。

  1. 读未提交(READ_UNCOMMITTED):读未提交,也叫未提交读。该隔离级别的事务可以看到其他事务中
    未提交的数据,会有脏读问题。
  2. 读提交(READ_COMMITTED):读已提交,也叫提交读。该隔离级别的事务能读取到已经提交事务的数据,该隔离级别不会有脏读的问题。
  3. 可重复读(REPEATABLE_READ):事务不会读到其他事务对已有数据的修改,即使其他事务已提交。也就可以确保同⼀事务多次查询的结果⼀致,但是其他事务新插⼊的数据,是可以感知到的,这也就引发了幻读问题。
  4. 串⾏化(SERIALIZABLE):序列化,事务最⾼隔离级别。它会强制事务排序,使之不会发⽣冲突,从⽽解
    决了脏读,不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。
2.2.2.1.3 propagation

事务的传播机制有以下七种。

我们以A方法调用B方法为例解释:

  1. Propagation.REQUIRED :默认的事务传播级别,如果当前存在事务,则加⼊该事务,如果当前没有事务,则创建⼀个新的事务。如果A方法有事务,B直接就加入,A没有事务B创建一个事务。
  2. Propagation.SUPPORTS :如果当前存在事务,则加⼊该事务。如果当前没有事务,则以⾮事务的⽅式继续运⾏。如果A方法有事务,B直接就加入,A没有事务B也没有事务。
  3. Propagation.MANDATORY :强制性。如果当前存在事务,则加⼊该事务。如果当前没有事务,则抛出异常。如果A方法有事务,B直接就加入,A没有事务抛异常。
  4. Propagation.REQUIRES_NEW :创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法都会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。如果A方法有事务,B创建一个事务,两个事务互不影响,A没有事务,B也会创建一个事务。
  5. Propagation.NOT_SUPPORTED :以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起(不⽤)。如果A方法有事务,B创建一个事务,使用B创建的事务,A没有事务B也没有。
  6. Propagation.NEVER :以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。如果A方法有事务,抛异常,A没有事务B也没有。
  7. Propagation.NESTED :如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏。如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED 。如果A方法有事务,B创建一个事务,A的事务相当于B的父事务,A没有事务,B也会创建一个事务。
传播行为 当前有事务 当前无事务
REQUIRED 加入事务 新建事务
SUPPORTS 加入事务 非事务执行
MANDATORY 加入事务 抛出异常
REQUIRES_NEW 挂起当前事务并新建事务 新建事务
NOT_SUPPORTED 挂起当前事务并以非事务执行 非事务执行
NEVER 抛出异常 非事务执行
NESTED 创建嵌套事务 新建事务
相关推荐
月夕·花晨28 分钟前
Gateway-过滤器
java·分布式·spring·spring cloud·微服务·gateway·sentinel
初听于你3 小时前
缓存技术揭秘
java·运维·服务器·开发语言·spring·缓存
恒悦sunsite5 小时前
Ubuntu之apt安装ClickHouse数据库
数据库·clickhouse·ubuntu·列式存储·8123
奥尔特星云大使6 小时前
MySQL 慢查询日志slow query log
android·数据库·mysql·adb·慢日志·slow query log
来自宇宙的曹先生6 小时前
MySQL 存储引擎 API
数据库·mysql
间彧6 小时前
MySQL Performance Schema详解与实战应用
数据库
间彧6 小时前
MySQL Exporter采集的关键指标有哪些,如何解读这些指标?
数据库
weixin_446260857 小时前
Django - 让开发变得简单高效的Web框架
前端·数据库·django
mpHH7 小时前
babelfish for postgresql 分析--todo
数据库·postgresql
zizisuo7 小时前
解决在使用Lombok时maven install 找不到符号的问题
java·数据库·maven