浅谈Spring6之事务场景(注解方式)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 事物:在一个业务流程中,通常需要多条DML(insert delete update)语句共同联合才能完成的,为了保证数据的安全,多条DML语句都必须同时成功,,或同时失败。

事物:在一个业务流程中,通常需要多条DML(insert delete update)语句共同联合才能完成的,为了保证数据的安全,多条DML语句都必须同时成功,,或同时失败。


事物的四个处理过程:开启事务、执行核心业务代码、提交事务、回滚事务


事务的四个特性:


原子性:事务是最小工作单位,不可再分割


一致性:事务要么同时成功,要么同时失败。事务前和事务后的总量不变


隔离性:事务和事务之间有隔离性,互不干扰


持久性:持久性是事务结束的标志


Spring实现事务的两种方式


编程式事务:编写代码来实现事务的管理


声明式事务*:基于注解的方式、基于xml配置方式



如有错误请指正,谢谢


以银行账户转账为案例

数据库表


12.png


spring6整合mybatis


pom.xml配置文件

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

   4.0.0

   org.example

   spring6-transaction-bank

   1.0-SNAPSHOT

   

       17

       17

       UTF-8

   

   

   jar

   

   

       

       

           repository.spring.milestone

           Spring Milestone Repository

           https://repo.spring.io/milestone

       

   

   

   

       

           jakarta.annotation

           jakarta.annotation-api

           2.1.0

       

       

       

           org.springframework

           spring-context

           6.0.0-M2

       

       

       

           org.springframework

           spring-jdbc

           6.0.0-M2

       

       

       

           mysql

           mysql-connector-java

           8.0.17

       

       

       

           org.mybatis

           mybatis

           3.5.11

       

       

       

       

           org.mybatis

           mybatis-spring

           3.0.1

       

       

       

           com.alibaba

           druid

           1.2.15

       

       

       

           org.springframework

           spring-test

           6.0.0-M2

           

       

       

       

           org.junit.jupiter

           junit-jupiter-api

           5.8.2

           test

       

   

   

       

           

               

               src/main/java

               

                   

                   **/*.properties

                   **/*.xml

               

               false

           

       

   



jdbc.properties文件

jdbc.driver=com.mysql.cj.jdbc.Driver

jdbc.url=jdbc:mysql://IP:3306/mysql

jdbc.user= root

jdbc.password= xxxxxx

mybatis-config.xml配置文件


       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

       "https://mybatis.org/dtd/mybatis-3-config.dtd">

   

       

       

   

   

       

   



SpringConfig.xml配置文件

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xmlns:context="http://www.springframework.org/schema/context"

      xmlns:tx="http://www.springframework.org/schema/tx"

      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

                          http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd">

       

       

       

       

       

       

               

               

               

               

       

       

       

               

               

               

               

               

               

       

               

       

               

               

       

               

       

               

       

               

       



BankDao接口

public interface BankDao {

   /**

    * 根据账号查询账户信息

    */

   List selectAll();

   /**

    * 根据actno查询账户信息

    * @param actno

    * @return

    */

   Bank selectByActno(String actno);

   /**

    * 更新账户信息

    * @param act

    * @return

    */

   int update(Bank act);

}


BankDao.xml


       PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

       "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

           select * from bank    

           select actno,balance from bank where actno = #{actno}    

   

       update bank set  balance=#{balance}  where actno =#{actno}

   



pojo实体类

package com.qgs.pojo;

/**

* @author QGS

* @version 1.0.0

* @date 2023年03月24日 16:36:58

* @packageName com.qgs.pojo

* @className Bank

* @describe TODO

*/

public class Bank {

   private String actno;

   private Double balance;

   public Bank() {

   }

   public Bank(String actno, Double balance) {

       this.actno = actno;

       this.balance = balance;

   }

   public String getActno() {

       return actno;

   }

   public void setActno(String actno) {

       this.actno = actno;

   }

   public Double getBalance() {

       return balance;

   }

   public void setBalance(Double balance) {

       this.balance = balance;

   }

   @Override

   public String toString() {

       return "Bank{" +

               "actno='" + actno + '\'' +

               ", balance=" + balance +

               '}';

   }

}


BankService 接口

public interface BankService {

   /**

    * 转账方法

    * @param fromActno 转出账户

    * @param toActno 转入账户

    * @param money 金额

    */

   void transfer(String fromActno ,String toActno ,double money);

}

BankService 接口实现类

package com.qgs.service;

import com.qgs.dao.BankDao;

import com.qgs.pojo.Bank;

import jakarta.annotation.Resource;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Propagation;

import org.springframework.transaction.annotation.Transactional;

/**

* @author QGS

* @version 1.0.0

* @date 2023年03月25日 11:15:24

* @packageName com.qgs.service

* @className BankServiceImpl

* @describe TODO

*/

@Service("bankServiceImpl")

//@Transactional

public class BankServiceImpl implements BankService{

   @Resource(name ="bankDao")

   private BankDao bankDao;

   @Override

   //@Transactional默认propagation = Propagation.REQUIRED

   @Transactional(propagation = Propagation.REQUIRED)

   public void transfer(String fromActno, String toActno, double money) {

       //查询转出账户余额

       Bank fromBank = bankDao.selectByActno(fromActno);

       if (fromBank.getBalance()

           throw new RuntimeException("余额不足");

       }

       //查询转入账户余额

       Bank toBank = bankDao.selectByActno(toActno);

       //将内存中转出与转入账户余额修改

       fromBank.setBalance(fromBank.getBalance() - money);

       toBank.setBalance(toBank.getBalance() + money);

       //更新数据库

       int count = bankDao.update(fromBank);

       int count2 = bankDao.update(toBank);

       count +=count2;

       if (count !=2){

           //回滚事务,转账失败

           throw new RuntimeException("失败");

       }

   }

}


@Test

   public void BankTest(){

       ApplicationContext applicationContext =new ClassPathXmlApplicationContext("SpringConfig.xml");

       BankService bankService = applicationContext.getBean("bankServiceImpl", BankService.class);

       try {

           bankService.transfer("act01","act2",300);

       }catch (Exception e){

           e.printStackTrace();

       }

   }

11.png


事务的传播行为


7.png8.png9.png





事务传播行为在spring框架中被定义为枚举类型。

事务的传播行为有7种:

REQUIRED:支持当前事务,如果不存在就新建一个(默认模式)

SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常。

REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起。

NOT_SUPPORTED:以非事务方式运行,如果事务存在,挂起当前事务。

NEVER:以非事务方式运行,如果有事务存在,抛出异常。

NESTED:如果当前争优一个事务在进行中,则该方法应当运行一个嵌套事务中。被嵌套的事务可以单独于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED。


数据库中读取数据存在的三大问题


脏读:读取到没有提交的数据库的数据。


不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。


幻读:读到的数据是假的。(事务并发,一定存在幻读)


spring事务隔离级别


5.png6.png




数据库中读取数据存在的三大读问题

脏读:读取到没有提交的数据库的数据。

不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。

幻读:读到的数据是假的。(事务并发,一定存在幻读)


事务隔离级别的四个级别:

读未提交:READ_UNCOMMITTEN

   这种隔离级别:存在脏读问题,所谓的脏读(dirty read)表示能够读取到其他事务未提交的数据。

读提交:READ_COMMITTED        (oracle)

   解决了脏读问题,其他事务提交之后才能读到,但存在不可重复读问题

可重复读:REPEATABLE_READ     (MYSQL)

   解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据移植都是一样的。但存在幻读问题。

序列化:SERIALLZABLE

   解决了幻读问题,事务排毒执行。不支持并发。

4.png



Spring事务超时

@Transactional(timeout = 10)   表示设置事务的超时时间为10秒。


超过10秒如果该事务所有的DML语句还没有执行完毕,最终选择回滚。

在当前事务中,最后一条DML语句执行之前的时间。最后一条DML语句后面还有很多业务逻辑(非DML语句),这些业务代码执行的时间不被记入超时时间

默认值为:timeout = -1 、表示没有时间限制。

3.png



Spring启动只读事务

@Transactional(readOnly = true)  作用:启动Spring优化策略,提高select语句执行效率

2.png


Spring事务设置异常回滚问题

设置RuntimeException异常时回滚

@Transactional(rollbackFor = RuntimeException.class)

设置NullPointerException异常不回滚

@Transactional(noRollbackFor = NullPointerException.class)


1.png


1.png

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
19天前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
40 2
Spring高手之路26——全方位掌握事务监听器
|
21天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
24天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
37 4
|
24天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
70 2
|
24天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
40 2
|
25天前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
|
27天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
32 2
|
24天前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
58 0
|
7月前
|
Java API Spring
Spring容器如何使用一个注解来指定一个类型为配置类型
Spring容器如何使用一个注解来指定一个类型为配置类型
61 0
|
2月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
51 0