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



相关文章
|
11天前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
24 1
Spring高手之路24——事务类型及传播行为实战指南
|
4天前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
13 3
|
2月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
2月前
|
Java 测试技术 数据库
Spring事务传播机制(最全示例)
在使用Spring框架进行开发时,`service`层的方法通常带有事务。本文详细探讨了Spring事务在多个方法间的传播机制,主要包括7种传播类型:`REQUIRED`、`SUPPORTS`、`MANDATORY`、`REQUIRES_NEW`、`NOT_SUPPORTED`、`NEVER` 和 `NESTED`。通过示例代码和数据库插入测试,逐一展示了每种类型的运作方式。例如,`REQUIRED`表示如果当前存在事务则加入该事务,否则创建新事务;`SUPPORTS`表示如果当前存在事务则加入,否则以非事务方式执行;`MANDATORY`表示必须在现有事务中运行,否则抛出异常;
140 4
Spring事务传播机制(最全示例)
|
1月前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
29 2
|
1月前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题
|
2月前
|
Java Spring
Spring 事务传播机制是什么?
Spring 事务传播机制是什么?
22 4
|
1月前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
44 0
|
3月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
3月前
|
前端开发 Java 数据库连接
一天十道Java面试题----第五天(spring的事务传播机制------>mybatis的优缺点)
这篇文章总结了Java面试中的十个问题,包括Spring事务传播机制、Spring事务失效条件、Bean自动装配方式、Spring、Spring MVC和Spring Boot的区别、Spring MVC的工作流程和主要组件、Spring Boot的自动配置原理和Starter概念、嵌入式服务器的使用原因,以及MyBatis的优缺点。