Spring 事务【Spring事务的定义与基本实现】

简介: Spring 事务【Spring事务的定义与基本实现】

🍎一.Spring事务定义

🍒1.1 什么是事务

事务:将⼀组操作封装成⼀个执行单元(封装到⼀起),要么全部成功,要么全部失败

🍒1.2 事务的作用


⽐如转账分为两个操作:

第⼀步操作:A 账户 -100 元

第⼆步操作:B 账户 +100 元

如果没有事务,第⼀步执⾏成功了,第⼆步执⾏失败了,那么 A 账户平⽩⽆故的 100 元就“⼈间蒸发”了。⽽如果使⽤事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败


🍎二.Spring 中事务的实现

Spring 中事务的实现有两种:

⭕ 编程式事务(手动写代码操作事务)

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

🍒2.1 Spring事务实现之前的准备

🍉2.1.1 创建项目时框架的选择


79f3be144b5740199f08122b17557c33.png


🍉2.1.2 application.yml配置文件

# 配置数据库连接
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: jj1432644716
    driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置日志打印 MyBatis 执行的 SQL
logging:
  level:
    com:
      example:
        demo: debug

🍉2.1.3 MyBatis的配置

在配置文件目录下创建一个mapper包,在创建一个关于Mybatis.xml文件

com.example.demo.mapper.UserMapper这取决于你配置文件目录下的包和文件名


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>


🍉2.1.4 创建对象和实现类

创建数据库内对象:

package com.example.demo.model;
import lombok.Data;
@Data
public class UserInfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
}

创建 Mapper接口:

package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
    public int add(UserInfo userInfo);
}

创建Service:

Service
public class UserService {
    @Resource
   private UserMapper userMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public int add(UserInfo userInfo){
        return userMapper.add(userInfo);
    }
}

创建Conctroller:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    // 在此方法使用编程式事务
    @RequestMapping("/add")
    public int add(UserInfo userInfo){
        // 要验证用户名和密码的{不为空}
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) return 0;
        int result = userService.add(userInfo);
        return result;
    }


🍒2.2 编程式事务(了解)


Spring 中的编程式事务的实现:



  1. 此方式包含了3个重要的操作:
    ● 获取事务(开启事务)
    ● 提交事务
    ● 回滚事务


  1. 依赖两个重要的对象:
    ● DataSourceTransactionManager
    ● TransactionDefinition

以下代码实现:


@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    // 在此方法使用编程式事务
    @RequestMapping("/add")
    public int add(UserInfo userInfo){
        // 要验证用户名和密码的{不为空}
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())||                                                                               !StringUtils.hasLength(userInfo.getPassword())) return 0;
        // 开启事务
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
        int result = userService.add(userInfo);
        System.out.println("add 受影响的行数:" + result);
        // 提交事务 / 回滚事务
        // transactionManager.rollback(transactionStatus); // 回滚业务
        transactionManager.commit(transactionStatus); // 提交事务
        return result;
    }

🍒2.3 声明式事务(@Transactional )


声明式事务的实现很简单,只需要在需要的⽅法上添加 @Transactional 注解就可以实现了,⽆需⼿动开启事务和提交事务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务,具体实现代码如下:


@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    // @Transactional注解只能在 public方法上生效
    @Transactional() 
    //在方法之前,自动开启事务,方法执行完之后自动提交事务,如果出现异常,则自动进行事务回滚
    @RequestMapping("/add2")
    // 要验证用户名和密码的{不为空}
    public int add2(UserInfo userInfo){
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())||  !StringUtils.hasLength(userInfo.getPassword())) return 0;
        int result = userService.add(userInfo);
        return result;
    }

🍉2.3.1 @Transactional 的作用域

@Transactional 可以⽤来修饰⽅法或类:

● 修饰⽅法时:需要注意只能应⽤到 public ⽅法上,否则不⽣效。推荐此种⽤法

● 修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效


🍉2.3.2 @Transactional 注解内的参数


参数 作用
value 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
transactionManager 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
propagation 事务的传播行为,默认值为Propagation.REQUIRED
isolation 事务的隔离级别,默认值为Isolation.DEFAULT
timeout 事务的超时时间,默认值为-1.如果超过该时间限制但事务还没有完成,则自动回滚事务.
readOnly 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为true.
rollbackFor 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
rollbackForClassName 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
noRollbackFor 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型.
noRollbackForClassName 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型.



🍉2.3.3 @Transactional 在异常被捕获的情况下


image.png


@Transactional 在异常被捕获的情况下,不会进⾏事务⾃动回滚,验证以下代码是否会发⽣事务回滚:

 // @Transactional注解只能在 public方法上生效
    @Transactional(isolation = Isolation.DEFAULT,timeout = 1) //在方法之前,自动开启事务,方法执行完之后自动提交事务,如果出现异常,则自动进行事务回滚
    @RequestMapping("/add2")
    // 要验证用户名和密码的{不为空}
    public int add2(UserInfo userInfo){
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) return 0;
        int result = userService.add(userInfo);
        System.out.println("add 受影响的行数:" + result);
        try {
            // 执⾏了异常代码(0不能做除数)
            int i = 10 / 0;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

我们发现确实是进行了异常处理后,到那时数据库还是有新增数据,没有进行回滚


image.png


image.png


🍈2.3.3.1 解决方案一(异常重新抛出)

 // @Transactional注解只能在 public方法上生效
    @Transactional(isolation = Isolation.DEFAULT,timeout = 1) //在方法之前,自动开启事务,方法执行完之后自动提交事务,如果出现异常,则自动进行事务回滚
    @RequestMapping("/add2")
    // 要验证用户名和密码的{不为空}
    public int add2(UserInfo userInfo){
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) return 0;
        int result = userService.add(userInfo);
        System.out.println("add 受影响的行数:" + result);
        try {
            // 执⾏了异常代码(0不能做除数)
            int i = 10 / 0;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            // 重新抛出异常
            throw e;
        }
        return result;
    }

42a1e65c61074128943904cf7f9077d7.png3587793c221d44b2875acbc189c13158.png

🍈2.3.3.2 解决方案二(⼿动回滚事务)

使用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();方法

 // @Transactional注解只能在 public方法上生效
    @Transactional(isolation = Isolation.DEFAULT,timeout = 1) //在方法之前,自动开启事务,方法执行完之后自动提交事务,如果出现异常,则自动进行事务回滚
    @RequestMapping("/add2")
    // 要验证用户名和密码的{不为空}
    public int add2(UserInfo userInfo){
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) return 0;
        int result = userService.add(userInfo);
        System.out.println("add 受影响的行数:" + result);
        try {
            // 执⾏了异常代码(0不能做除数)
            int i = 10 / 0;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            // ⼿动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

f594d98325c54ec7982d3c48662e6b44.png


我们在数据库中查询看到,虽然没有报错,但是事务回滚了


0131b00718284f68b1eadb685cbbac75.png


🍉2.3.4 @Transactional ⼯作原理(默认是 JDK动态代理)


@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理

@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务

@Transactional 实现思路预览:


19ff5d4c4d04456c96c8c99a8a3bda7f.png259e2017641d4c9aa3d7f685c816f75a.jpg



相关文章
|
1月前
|
监控 Java 数据处理
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
|
1月前
|
Java 数据库 Spring
Spring事务失效的场景详解
Spring事务失效的场景详解
33 0
|
1月前
|
Java 数据库 Spring
Spring事务的传播机制(行为、特性)
Spring事务的传播机制(行为、特性)
36 0
|
2月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
35 1
|
1月前
|
XML Java 数据库
【二十四】springboot整合spring事务详解以及实战
【二十四】springboot整合spring事务详解以及实战
106 0
|
1月前
|
Java 数据库 开发者
|
2月前
|
Java 数据库 数据安全/隐私保护
|
2月前
|
Java 关系型数据库 MySQL
深入分析Spring事务和底层原理
深入分析Spring事务和底层原理
39 1
|
2月前
|
Java 数据库 开发者
一文带你掌握Spring事务核心:TransactionDefinition详解!
TransactionDefinition是Spring框架中用于定义事务属性的核心接口。在Spring的事务管理中,这个接口扮演着至关重要的角色,它允许开发者定制事务的各种属性,如隔离级别、传播行为、超时时间以及是否只读。
一文带你掌握Spring事务核心:TransactionDefinition详解!
|
2月前
|
XML 运维 Java
spring事务(3)基于XML的声明式事务
spring事务(3)基于XML的声明式事务
17 0