【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 创建嵌套事务 新建事务
相关推荐
睡觉z2 小时前
MySQL数据库初体验
数据库·mysql·oracle
peerless_fu3 小时前
rabbitmq AI复习
spring·rabbitmq
CC同学呀3 小时前
从0到1:多医院陪诊小程序开发笔记(上)
数据库·笔记
十年砍柴---小火苗3 小时前
gin使用Mysql连接池用法
数据库·mysql·gin
天河归来3 小时前
通过阿里云服务发送邮件
数据库·阿里云·云计算
Chasing__Dreams4 小时前
Redis--基础知识点--28--慢查询相关
数据库·redis·缓存
C182981825754 小时前
小表驱动大表更快吗,不是
数据库
人生导师yxc4 小时前
SpringCloud基础知识
后端·spring·spring cloud
烟沙九洲4 小时前
@ModelAttribute、@RequestBody、@RequestParam、@PathVariable 注解对比
java·spring
ademen5 小时前
spring4第4课-ioc控制反转-详解如何注入参数
java·后端·spring