Spring学习笔记13 Spring对事务的支持

Spring学习笔记12 面向切面编程AOP-CSDN博客

什么是事务:在一个业务流程当中,通常需要多条DML(insert delete update)语句共同联合才能完成,这多条DML语句必须同时成功,或者同时失败,这样才能保证数据的安全.

多条DML要么同时成功,要么同时失败,叫做事务(Transaction)

事务四个处理过程:

1.开启事务(start transaction)

2.执行核心业务代码

3.提交事务(如果核心业务处理过程中没有出现异常)(commit transaction)

4.回滚事务(如果核心业务处理过程中出现异常)(rollabck transaction)

事务的四个特性:

A.原子性:事务是最小的工作单元,不可再分

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

I.隔离性:事务和事务之间因为有隔离,才可以保证互不干扰

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

引入事务场景:

以银行账户转账为例学习事务.两个账户act-01和act-02.

act-01向act-02转账10000.

一个账户减10000,一个账户加10000,必须同时成功,或者同时失败

连接数据库的技术采用Spring框架的JdbcTemplate

新建maven项目或者模块

依赖

<dependencies><!--spring依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.10</version></dependency><!--jdbcTemplate依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.10</version></dependency><!--mysql驱动依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!--druid德鲁伊依赖--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.13</version></dependency><!--javaee的注解 @Resource依赖--><dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version></dependency><!--单元测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency>
</dependencies>

准备数据库表

项目结构

实体类

package com.example.pojo;import java.util.Objects;/*** @author hrui* @date 2023/9/26 15:02*/
public class Account {private Integer id;private String actno;private Double balance;public Account() {}public Account(Integer id, String actno, Double balance) {this.id = id;this.actno = actno;this.balance = balance;}@Overridepublic String toString() {return "Account{" +"id=" + id +", actno='" + actno + '\'' +", balance=" + balance +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Account account = (Account) o;return Objects.equals(id, account.id) && Objects.equals(actno, account.actno) && Objects.equals(balance, account.balance);}@Overridepublic int hashCode() {return Objects.hash(id, actno, balance);}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}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;}
}

持久层

package com.example.dao;import com.example.pojo.Account;/*** 专门负责账户信息的CRUD操作* DAO中只执行SQL语句,没有任何业务逻辑.* 也就是说DAO不和业务挂钩* @author hrui* @date 2023/9/26 15:00*/
public interface AccountDao {Account selectByActNo(Integer id);int updateAct(Account account);
}

持久层实现类

package com.example.dao.impl;import com.example.dao.AccountDao;
import com.example.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;/*** @author hrui* @date 2023/9/26 15:04*/
@Repository
public class AccountDaoImpl implements AccountDao {@Resource(name="jdbcTemplate")private JdbcTemplate jdbcTemplate;@Overridepublic Account selectByActNo(Integer id) {String sql="select id,actno,balance from t_act where id=?";Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), id);return account;}@Overridepublic int updateAct(Account account) {String sql="update t_act  set balance=?  where id=?";int count = jdbcTemplate.update(sql, account.getBalance(),account.getId());return count;}
}

业务层接口

package com.example.service;/*** @author hrui* @date 2023/9/26 15:55*/
public interface AccountService {void transfer(Integer fid,Integer tid,double balance);
}

业务层实现类

package com.example.service.impl;import com.example.dao.AccountDao;
import com.example.pojo.Account;
import com.example.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;/*** @author hrui* @date 2023/9/26 15:57*/
@Service
public class AccountServiceImpl implements AccountService {@Resource(name="accountDaoImpl")private AccountDao accountDao;@Overridepublic void transfer(Integer fid, Integer tid, double balance) {//查询转出账户余额够不够Account fAccount = accountDao.selectByActNo(fid);if(fAccount.getBalance()<balance){throw new RuntimeException("余额不足");}//余额充足Account tAccount = accountDao.selectByActNo(tid);//修改内存中两个对象的值fAccount.setBalance(fAccount.getBalance()-balance);tAccount.setBalance(tAccount.getBalance()+balance);int count = accountDao.updateAct(fAccount);//模拟异常String str=null;System.out.println(str.toString());count+=accountDao.updateAct(tAccount);if(count!=2){System.out.println("转账失败,联系银行");}}
}

Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--组件扫描--><context:component-scan base-package="com.example"></context:component-scan><!--配置数据源--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="1:3306/spring6"></property><property name="username" value="1"></property><property name="password" value="1"></property></bean><!--配置JdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>
</beans>

测试类

import com.example.service.AccountService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author hrui* @date 2023/9/26 16:14*/
public class Test {@org.junit.Testpublic void transfer(){BeanFactory beanFactory=new ClassPathXmlApplicationContext("spring-config.xml");AccountService accountServiceImpl = beanFactory.getBean("accountServiceImpl", AccountService.class);try {accountServiceImpl.transfer(1, 2, 50);System.out.println("转账成功");} catch (Exception e) {e.printStackTrace();}}
}

上面代码中间抛出异常就会导致,一遍转账了,而另一边没收到的情况

以代码逻辑的方式  需要在下面代码中执行1.开启事务  2.执行核心业务逻辑  3.无异常则提交事务

4.有异常则回滚事务

  @Overridepublic void transfer(Integer fid, Integer tid, double balance) {//1.开启事务//2.执行核心业务逻辑//查询转出账户余额够不够Account fAccount = accountDao.selectByActNo(fid);if(fAccount.getBalance()<balance){throw new RuntimeException("余额不足");}//余额充足Account tAccount = accountDao.selectByActNo(tid);//修改内存中两个对象的值fAccount.setBalance(fAccount.getBalance()-balance);tAccount.setBalance(tAccount.getBalance()+balance);int count = accountDao.updateAct(fAccount);//模拟异常String str=null;System.out.println(str.toString());count+=accountDao.updateAct(tAccount);if(count!=2){System.out.println("转账失败,联系银行");}//3.如果执行业务流程过程中,没有异常.提交事务//4.如果执行业务流程过程中,有异常,回滚事务}

Spring对事务的支持

Spring实现事务的两种方式

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

声明式事务:1.基于注解方式   2.基于XML配置方式

Spring事务管理API

Spring对事务的管理底层实现方式是基于AOP实现的.采用AOP的方式进行了封装.所以Spring专门针对事务开发了一套API,API的核心接口如下

PlatformTransactionManager接口:Spring事务管理器的核心接口.在Spring6中它有两个实现

1.DataSourceTransactionManager:支持JdbcTemplate,Mybatis,Hibernate等事务管理

2.JtaTransactionManager:支持分布式事务管理

如果要在Spring6中使用JdbcTemplate,就要使用DataSourceTransactionManager来管理事务.(Srping内置写好了,可以直接使用)

声明式事务基于注解的实现方式

Spring配置文件里配置事务管理器,让SpringIOC容器管理  设置dataSource属性为druid的DataSource实现类

然后在方法上加@Transactional即可

好比有了1 2 3 4的步骤

写在类上,类里面所有方法都有事务控制

写在方法上,单个方法有事务控制

@Transactional注解

事务的传播行为

在a()方法中调用了b()方法,比如a()方法有事务,b()方法也有事务,那么事务是如何传递的?是合并到一个事务?还是另开启一个事务?这就是事务的传播行为

事务一共有七种传播行为:

REQUIRED:支持当前事务,如果不存在就新建一个(默认)[没有就新建,有就加入]

SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行[有就加入,没有就不管了]

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

REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起[不管有没有事务,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起]

NOT_SUPPORTED:以非事务方式运行,如果有事务在,挂起当前事务[不支持事务,存在就挂起]

NEVER:以事务方式运行,如果有事务存在,挂起当前事务[不支持事务,存在就挂起]

NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套事务中.被嵌套的事务可以独立于外层事务进行提交或回滚.如果外层事务不存在,行为就像REQUIRED一样[有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚,没有事务就和REQUIRED一样]

默认是@Transactional(Propagation=Propagation.REQUIRED)

下面两个是常用的:

REQUIRED:支持当前事务,如果不存在就新建一个(默认)[没有就新建,有就加入]

REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起[不管有没有事务,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起]

举例:    A方法和B方法的方法上都有@Transactional(Propagation=Propagation.REQUIRED)默认

在A方法中调用B方法

A(){

        保存操作XXXX;

        B();

}

B方法

B(){

        保存操作

}

那么A方法和B方法在同一个事务中, 即使你在A方法里对B方法进行try catch,无论哪个方法里报错,都会回滚

举例:    A方法是@Transactional(Propagation=Propagation.REQUIRED)默认,B方法的方法上是@Transactional(Propagation=Propagation.REQUIRES_NEW)

A方法内进行保存操作且调用了B方法,假如B方法报错了,且A方法没有对B方法进行try catch那么两个都会回滚,假如B方法报错了,但是A方法内对B方法进行了try catch那么B方法会回滚,而A方法不会回滚,因为是两个事务

事务隔离级别:

数据库中读取数据的三大问题:(三大读问题)

脏读:读取到没有提交的数据,叫脏读(读的是缓存(内存里的东西))

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

幻读:督导的数据是假的

事务隔离级别包括四个级别:

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

读提交:READ_COMMITTED.解决了脏读问题,其他事务提交之后才能督导,但存在不可重复读问题.(Oracle数据库的默认级别)

可重复读:REPEATABLE_READ.解决了不可重复读,可以达到可重复读的效果,只要当前事务不结束,读取到的数据一直都是一样的.但存在幻读问题(Mysql数据库的默认级别)

序列化:SERIALIZABLE.解决了幻读问题,事务排队执行.不支持并发

读未提交,读已提交,可重复读都是多线程并发问题引起的,序列化就排队

事务超时问题     @Transactional(timeout=10)

以上代码表示设置事务的超时时间为10秒

表示超过10秒如果该事务中所有的DML(增删改)语句还没有执行完毕,最终结果会选择回滚

默认值-1,表示没有时间限制

这里有个坑,事务的超时时间指的是哪段时间?

在当前事务当中,最后一条DML(增删改)语句执行之前的时间,如果最后一条DML语句后面还有很多业务逻辑,这些业务代码执行的时间不会被计入超时时间

只读事务   代码   @Transactional(readOnly=true)

将当前事务设置为只读事务,在该事务执行过程中只允许select语句执行,delete insert update均不可以执行.

该特性的作用:启动Spring的优化策略,提高select语句执行效率.

如果该事务中确实没有增删改操作,建议设置为只读事务.

异常回滚事务:

代码 例 @Transactional(rollbackFor=NumberFormatException.class)

表示只有发生NumberFormatException异常或该异常的子类异常时才回滚

设置哪些异常不回滚事务:

代码 例 @Transactional(noRollbackFor=NullPointerException.class)

表示发生NullPointerException或该类子类异常不回滚,其他异常则回滚

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88690.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM IIC总线实现温湿传感器

IIC.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h"/* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4* I2C1_SCL ---> PF14* I2C1_SDA ---> PF15** */#define SET_SDA_OUT do{G…

机器人过程自动化(RPA)入门 3. 顺序、流程图和控制流程

到目前为止&#xff0c;我们已经了解了RPA是什么&#xff0c;并且我们已经看到了通过记录任务的活动并运行它来训练UiPath机器人是多么简单。使用记录器的UiPath可以很容易地自动化日常任务。在我们开始自动化复杂的任务之前&#xff0c;让我们学习如何控制从一个到另一个的活动…

Linux命令之chattr命令

一、chattr命令简介 chattr命令用于更改文件或目录的属性&#xff0c;包括不可修改属性、同步属性、追加属性、无尽属性、压缩属性、无尽属性、不可删除属性等。chattr命令只能由超级用户或文件的所有者使用。 二、chattr命令使用示例 1、给文件设置版本 -v参数设置版本信息只…

12KM02E-V0002 3EGM030300R0002 模块化和加固的边缘计算加速

12KM02E-V0002 3EGM030300R0002 模块化和加固的边缘计算加速 随着边缘人工智能解决方案的兴起&#xff0c;对实时洞察和自主决策的需求显著增长。这也带来了对变革性技术的高度需求&#xff0c;这些技术可以在坚固的边缘支持和提供最佳性能。为了应对技术革命&#xff0c;Prem…

linux使用操作[3]

文章目录 版权声明环境变量$符号自行设置环境变量 上传、下载rz、sz命令 压缩、解压tar命令压缩tar解压zip 命令压缩文件unzip 命令解压文件 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明&#xff0c;所有版权属于黑马程序员或相关权利人…

了解MES:提升制造业的效率与竞争力

今天我将和大家分享关于MES&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;的知识。随着制造业的发展和变革&#xff0c;MES作为一个关键的信息技术工具&#xff0c;已经成为许多企业提升效率和竞争力的重要策略之一。 MES的定义与作用 MES是…

Redis集群架构搭建——主从、哨兵、集群

上一篇文章Ubuntu上通过源码方式安装Redis已经介绍了如何安装redis&#xff0c;在这篇文章中&#xff0c;将会教大家搭建Redis的几种高可用的架构&#xff1a;主从架构、哨兵集群、Cluster集群。 本篇文章使用的redis版本为6.2.13&#xff0c;不同版本的配置可能有略微的区别&a…

HTTP代理SSL连接:保障网络安全的重要协议

HTTP代理SSL连接是一种网络安全协议&#xff0c;它结合了HTTP代理和SSL/TLS协议&#xff0c;用于在客户端和服务器之间建立加密通信通道。HTTP代理SSL连接可以保护数据在传输过程中不被窃听、篡改或伪造&#xff0c;从而确保数据的完整性、保密性和可靠性。在本文中&#xff0c…

目前很火的养猫微信小程序源码带流量主+搭建教程

目前很火的养猫微信小程序源码带流量主搭建教程。 搭建教程 进入小程序我们下载开发者工具 开发者工具安装好了 我们就把前端源码导入进开发者工具中 这里的APPID我们填写自己的小程序APPID 修改siteinfo.js里的uniacid和acid 这两个ID在刚才后端添加的小程序那里看 在把…

新版WordPress系统文章自动采集插件/Auto Post pro完美运行版/多线程采集(wp自动采集)

源码介绍&#xff1a; 最新版WordPress系统文章自动采集插件&#xff0c;它是一款帮助用户提供方便快捷的文章自动采集方案的插件。WordPress自动采集插件&#xff0c;让内容采集变得高效便捷。作为Auto Post pro完美运行版&#xff0c;这里分享的是WordPress文章采集插件Auto…

TG Pro for Mac强大的硬件温度检测、风扇控制工具测评

无论您是旧机型还是全新MacBookPro&#xff0c;使用TG Pro均可延长Mac的使用寿命。小编就给大家详细说一下使用TG Pro的体验~ 打开TG Pro&#xff0c;您会注意到的第一件事是带有大量温度&#xff0c;风扇速度和诊断信息的主窗口。 这是您将与之交互的应用程序的主要区域之一。…

Selenium自动化测试 —— 通过cookie绕过验证码的操作!

验证码的处理 对于web应用&#xff0c;很多地方比如登录、发帖都需要输入验证码&#xff0c;类型也多种多样&#xff1b;登录/核心操作过程中&#xff0c;系统会产生随机的验证码图片&#xff0c;进行验证才能进行后续操作 解决验证码的方法如下&#xff1a; 1、开发做个万能…

设计模式 - 代理模式

目录 一. 前言 二. 实现 三. 静态代理和动态代理 一. 前言 代理模式&#xff08;Proxy Pattern&#xff09;&#xff0c;为某个对象提供一种代理以控制对对象的访问。即客户端可通过代理对象间接访问目标对象&#xff0c;同时可限制、增强、修改目标对象的一些特性。访问者不…

vue3 - 按需导入使用Element Plus图标、iconify图标、本地SVG/PNG图标

GitHub Demo 地址 在线预览 vue3 - 按需导入使用Element Plus图标、iconify图标、本地SVG/PNG图标 [GitHub Demo 地址](https://github.com/iotjin/jh-vue3-admin)[在线预览 ](https://iotjin.github.io/jh-vue3-admin) 一、iconify插件安装使用效果图 二、通过自动导入使用ic…

AIGC: 区块链与数据安全

随着国家将区块链纳入战略发展规划&#xff0c;数字经济蓬勃发展。近年来&#xff0c;数据的流通成为了实体经济赋能的关键&#xff0c;而在这一过程中&#xff0c;区块链技术和数据安全变得至关重要。 中国已经成为全球最大的数据体&#xff0c;每天产生大量数据。数字经济已…

软件测试人员必须知道的接口测试基础

一、首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接口&#xff0c;一种是系统对外的接口。系统对外的接口&#xff1a;比如你要从别的网站或服务器上获取资源或信息&#xff0c;别人肯定不会把数据库共享给你&#xff0c;他只能…

【Linux】——基操指令(一)

个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 LeetCode刷题 算法专栏 目录 前言 基操前的碎碎念 计算机的层状结构 基础指令 查看登录用户指令 查看用户指令 查看当前所处工作目录 清屏指令 基操指令 ls命令 cd命令 makdir指令 rmdir指令 &…

Mac电脑信息大纲记录软件 OmniOutliner 5 Pro for Mac中文

OmniOutliner 5 Pro是一款专业级的Mac大纲制作工具&#xff0c;它可以帮助用户更好地组织和管理信息&#xff0c;以及制作精美的大纲。以下是OmniOutliner 5 Pro的主要功能和特点&#xff1a; 强大的大纲组织和管理功能。OmniOutliner 5 Pro为用户提供了多层次的大纲结构&…

SpringBoot+MinIO8.0开箱即用的启动器

一、代码拉取及安装 1.码云地址 https://gitee.com/qiangesoft/rdp-starter/tree/master/rdp-starter-minio 2.本地安装 代码接入 1.引入依赖 <dependency><groupId>com.qiangesoft.rdp</groupId><artifactId>rdp-starter-minio</artifactId&g…

Learn Prompt- Midjourney案例:动漫设计

使用 Midjourney 生成动漫有两种方法&#xff1a;使用Niji模式或使用标准的 Midjourney 模型。Niji V5 是 Midjourney 的动漫专用模型。它建立在标准 Midjourney 模型的全新架构之上&#xff0c;更擅长生成命名的动漫角色。Niji V4于2023年12月发布&#xff0c;Niji V5于2023年…