spring 事务隔离级别和传播行为_Java工程师面试1000题146-Spring数据库事务传播属性和隔离级别...

146、简介一下Spring支持的数据库事务传播属性和隔离级别

介绍Spring所支持的事务和传播属性之前,我们先了解一下SpringBean的作用域,与此题无关,仅做一下简单记录。

在Spring中,可以在元素的scope属性中设置bean的作用域,来决定这个bean是单实例的还是多实例的。默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一的实例,整个IOC容器范围内都可以共享该实例;所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例,该作用域被称为singleton,他是所有bean的默认作用域。

  1. singleton:在SpringIOC容器中仅存在一个bean实例,Bean以单实例的方式存在
  2. prototype:每次调用getBean()时都会返回一个新的实例
  3. request:每次HTTP请求都会创建一个新的Bean。该作用域仅适用于WebApplicationContext环境。
  4. session:同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。该作用域仅适用于WebApplicationContext环境。

介绍完Spring Bean的作用域之后,下面开始进入正题——Spring支持的数据库事务传播属性和隔离级别

1、事务的传播属性

首先我们先了解一下什么是事务的传播属性(传播行为):当一个事务方法被被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

事务的传播行为是由传播属性来指定的。

propagation:用来设置事务的传播行为:一个方法运行在了一个开启了事务的方法中时,当前方法是使用原来的事务,还是开启一个新的事务,这就是事务的传播行为。

比如:Propagation.REQUIRED:默认值,代表继续使用原来的事务;Propagation.REQUIRES_NEW:将原来的事务挂起,开启一个新的事务。最常用的事务传播属性就是REQUIRED和REQUIRES_NEW,下面就通过编程来进行测试。

首先,在数据库里面新建三张表:

CREATE DATABASE /*!32312 IF NOT EXISTS*/`location` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `location`;DROP TABLE IF EXISTS `account`;CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(30) DEFAULT NULL, `balance` float unsigned DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;insert into `account`(`id`,`username`,`balance`) values (1,'HanZong',100);DROP TABLE IF EXISTS `book`;CREATE TABLE `book` ( `isbn` varchar(20) DEFAULT NULL, `name` varchar(20) DEFAULT NULL, `price` float DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into `book`(`isbn`,`name`,`price`) values ('1001','Spring',60),('1002','SpringMVC',50);DROP TABLE IF EXISTS `book_stock`;CREATE TABLE `book_stock` ( `isbn` varchar(20) DEFAULT NULL, `stock` int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into `book_stock`(`isbn`,`stock`) values ('1001',100),('1002',100);

然后,搭建Spring的开发环境,具体配置在这里不再讲解了,不是本知识点的重点。然后新建三个接口,三个实现类。

Cashier接口:

import java.util.List;public interface Cashier { //去结账的方法 void checkout(int userId, List isbns);}
实现类:import com.spring.transaction.BookShopService;import com.spring.transaction.Cashier;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service("cashier")public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional @Override public void checkout(int userId, List isbns) { for (String isbn : isbns){ //调用BookShopService中的买东西方法 bookShopService.purchase(userId,isbn); } }}
BookShopService接口:public interface BookShopService { //定义一个买东西方法 void purchase(int userId,String isbn);}

实现类:

import com.spring.transaction.BookShopDao;import com.spring.transaction.BookShopService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Servicepublic class BookShopServiceImpl implements BookShopService {  @Autowired private BookShopDao bookShopDao;  @Transactional @Override public void purchase(int userId, String isbn) { //1.获取要买的图书的价格 double bookPrice = bookShopDao.getBookPriceByIsbn(isbn);System.out.println(bookPrice); //2.更新图书的库存 bookShopDao.updateBookStock(isbn); //3.更新用户的余额 bookShopDao.updateAccountBalance(userId, bookPrice);double bookPriceByIsbn = bookShopDao.getBookPriceByIsbn(isbn);System.out.println(bookPriceByIsbn); }}

操作数据库的接口:

public interface BookShopDao {//根据书号查询图书的价格double getBookPriceByIsbn(String isbn);//根据书号更新图书的库存,每次只买一本图书void updateBookStock(String isbn);//根据用户的id和图书的价格更新用户的账户余额void updateAccountBalance(int userId, double bookPrice);}

实现类:

import com.spring.transaction.BookShopDao;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;@Repository("bookShopDao")public class BookShopDaoImpl implements BookShopDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic double getBookPriceByIsbn(String isbn) {// 写sql语句String sql = "select price from book where isbn = ?";// 调用JdbcTemplate中的queryForObject方法Double bookPrice = jdbcTemplate.queryForObject(sql, Double.class, isbn);return bookPrice;}@Overridepublic void updateBookStock(String isbn) {// 写sql语句String sql = "update book_stock set stock = stock - 1 where isbn = ?";// 调用JdbcTemplate中的update方法jdbcTemplate.update(sql, isbn);}@Overridepublic void updateAccountBalance(int userId, double bookPrice) {// 写sql语句String sql = "update account set balance = balance - ? where id = ?";// 调用JdbcTemplate中的update方法jdbcTemplate.update(sql, bookPrice, userId);}}

介绍一下上面的接口和实现类,BookShopService接口里面有一个买东西的方法purchase(),Cashier里面有一个checkout()方法,结账的方法,checkout()方法要调用purchase()方法来实现功能,checkout()方法上面添加了声明式事务注解@Transactional,purchase()方法上面也添加了声明式事务注解@Transactional。checkout()方法调用了purchase()方法,两个方法都使用了事务,这时候在运行的时候,purchase()到底是使用自己的事务呢,还是使用checkout()的事务呢?这个就属于事务的传播行为!

事务的传播行为可以使用@Transactional注解里面的一个propagation属性来设置。propagation可以设置以下7种属性值。

96fbb10bdac177a4d153f889a0836a5d.png

我们来看一下啊,purchase()方法运行在checkout()方法里面,按照Spring默认的事务传播属性为REQUIRED,那么purchase()方法就应该使用checkout()方法的事务,checkout()方法里面有一个for循环,可能会调用多次purchase方法,根据事务的原子性,多次执行purchase()方法要么全部成功,要么全部失败。我们写一个测试方法:

public class TestTX { //创建IOC容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); @Test public void testCashier(){ Cashier cashier = (Cashier) ioc.getBean("cashier"); //创建List List isbns = new ArrayList<>(); isbns.add("1001"); isbns.add("1002"); //去结账 cashier.checkout(1,isbns); }}

测试程序中,我们创建一个ArrayList,里面添加两个图书id,一个是1001,一个是1002,代表我们将要购买的图书,图书的价格保存在数据库中book表中,1001的价格是60,1002的价格是50,另一张表book_store里面存放的是图书库存,1001库存100本,1002库存100本,最后一张表是用户表account,里面就只有一个用户,用户余额100元,这个余额在建表的时候必须要设置为unsigned的,不能成为负数,否则就没法测试了。

现在是账户余额只有100元,要同时买两本1001和1002,明显差10元。现在我们来测试一下,到底是一本也买不成功,还是可以买成功一本。根据事务传播行为,没有设置就代表默认值,默认值就是REQUIRED:如果有事务在运行,当前的方法就在这个事务内运行,否则就开启一个新的事务,并在自己的事务内运行。也就是说,现在买1001和买1002在同一个事务里面,根据事务的原子性,要么都完成,要么都不完成,现在我的余额是100,可以买成功1001,不能卖成功1002,到底最终的结果是什么呢?让我们运行测试程序。报了一个异常:

Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Out of range value for column 'balance' at row 1

这句报异常就是由于在建表的时候把balance设置为unsigned的,使之不能成为负数。这不是我们关心的,我们关心的是数据库中库存和账户余额是否发生变化。我们刷新数据库表,发现余额没有改变,两本书都没有买成功。为什么会这样呢?我们再来分析一下。如果事务的传播行为是默认值的话,即我们没有在@Transactional注解里面设置,默认值就是REQUIRED,也就说是会使用checkout()方法原来的事务,虽然我们在purchase()上面也添加了事务,但是由于事务的传播行为是默认值,所以他会使用checkout()方法的事务,如果使用checkout()方法的事务,我们发现,在ArrayList里面有两本图书,买两本书调用的都是同一个purchase()方法,两次调用是在同一个事务里面,但是买完1001之后,再去买1002,失败了,根据事务的原子性,要么都完成,要么都不完成,所以,它要回滚事务,最终才造成了上面的结果。

那么,我们能不能让它买成功一本呢?可以,只需要把purchase()方法的事务传播行为改为REQUIRES_NEW。

@Transactional(propagation = Propagation.REQUIRES_NEW)

同样运行测试程序,还是报“Data truncation: Out of range value for column 'balance' at row 1”异常,不管他,我们刷新数据库,观察账户余额,发现变为了40,再看一下库存,1001的库存变为了99,1002的库存没有变还是100。这就说明我们买成功了一本。由于我们把purchase()方法的事务传播行为改为REQUIRES_NEW,就是每次调用都要开启一个新事物,虽然checkout()也设置了事务,但是我不用你的,每次都用我自己的,这就是事务之间的隔离性,互相之间没有影响,所以我们买1001和买1002的时候用到的就不是同一个事务了,购买1002失败不会导致购买1001也失败。所以最终的结果就是1001买成功了,1002没有买成功。

小总结:

  • REQUIRED传播行为:当bookService的purchase()方法被另外一个事务方法checkout()调用时,它会默认在现有的事务内运行。因此在checkout()方法的开始和结束内只有一个事务,这个事务只会在checkout()方法调用结束时被提交,那就导致用户一本都买不了。
0113b6928d737f4f3bcc1d6f4fccb051.png
7df465b65df3807e0a233ecdcb95fede.gif
  • REQUIRES_NEW传播行为:表示该方法必须启动一个新的事务,并在自己的事务内运行,如果已经有在运行,就先把他挂起。
0aa4ce95d3980479c3c022ba2da2565b.png
7df465b65df3807e0a233ecdcb95fede.gif

2、事务的隔离级别

在讲事务的隔离级别之前,我们先来看一下数据库事务并发问题:

假设现在有两个事务:Transaction01和Transaction02并发执行。

①脏读:当前事务读到了其他事务更新但是还没有提交的值(其他事务不回滚还好,其他事务回滚你读到的就是一个无效值)。

  1. Transaction01将某条记录的AEG值从20修改为30
  2. Transaction02读取了Transaction01更新后的值:30
  3. Transaction01回滚事务,AEG的值又恢复到了20
  4. Transaction02读取到的30就是一个无效的值

②不可重复读:

  1. Transaction01读取了AEG的值为20
  2. Transaction02将AEG的值修改为30
  3. Transaction01再次读取AEG值为30,和第一次读取结果不一致

③幻读:

  1. Transaction01读取了STUDENT表中的一部分数据
  2. Transaction02向STUDENT表中插入了新的行

事务的隔离级别:数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度成为事务的隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但是并发性就越弱。

1、读未提交:READ UNCOMMITTED,允许Transaction01读取Transaction02未提交的修改。(脏读、不可重复读、幻读都有可能出现)

2、读已提交:READ COMMITTED,要求Transaction01只能读取Transaction02已经提交的修改。(脏读就可以避免了)

3、可重复读:REPEATABLE READ,确保Transaction01可以多次从一个字段读取到相同的值,即Transaction01执行期间禁止其他事务对这个字段进行更新。(脏读、不可重复读都不会出现了)

4、串行化:SERIALIZABLE,确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其他事务对这个表进行添加、更新、删除操作。可以避免所有并发问题,但是性能最低。(脏读、不可重复读、幻读都不可能出现)

acb0f9527b9c2fdc6ce1aedcb440e4e1.png

各数据库产品对事务隔离级别的支持程度:

0f553461d958cde4e2fe15773d286c8f.png

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

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

相关文章

长江存储发布PCle4.0 固态硬盘致态TiPro7000,顺序读取7400MB/s

2021年12月29日&#xff0c;长江存储重磅发布全新消费级旗舰固态硬盘产品致态TiPro7000。该产品采用基于Xtacking&#xff08;晶栈&#xff09; 2.0架构的长江存储第三代三维闪存芯片&#xff0c;支持PCle Gen4x4接口、NVMe 1.4协议&#xff0c;顺序读取速度高达7400MB/s。该产…

图像ISP处理——畸变校正算法

图像畸变校正算法主要用于矫正图像中因为摄像机镜头畸变而引起的形状和尺寸变化。摄像机镜头畸变主要包括径向畸变和切向畸变。以下是一些常见的图像畸变校正算法&#xff1a; 多项式畸变校正法&#xff08;Polynomial Distortion Correction&#xff09;&#xff1a; 原理&am…

KubeDL 加入 CNCF Sandbox,加速 AI 产业云原生化

简介&#xff1a; 2021 年 6 月 23 日&#xff0c;云原生计算基金会&#xff08;CNCF&#xff09;宣布通过全球 TOC 投票接纳 KubeDL 成为 CNCF Sandbox 项目。KubeDL 是阿里开源的基于 Kubernetes 的 AI 工作负载管理框架&#xff0c;取自"Kubernetes-Deep-Learning"…

预登录握手失败_英雄联盟手游登录问题汇总

1、出现“无法安装完成”的情况已经获取了资格&#xff0c;但出现“无法安装完成”的情况&#xff0c;譬如谷歌商店的下载界面一直闪退、下载没进度、卡在安装中、卡在等待中怎么办?可前往第三方下载软件&#xff0c;(推荐介绍GamesToday)下载游戏。2、提示&#xff1a;目前还…

云云协同解决方案全景图发布 华为云助力科技企业云上创新

12月29日&#xff0c;以“云云协同 共创云上新价值”为主题的华为云&华为终端云服务创新峰会2022在京圆满召开。华为云与产业专家和企业代表们共同探讨了在产业数字化机遇与挑战并存的新形势下&#xff0c;如何推动产业升级&#xff0c;共创新价值。 会上&#xff0c;面向科…

解密万亿参数M6模型预训练背后的分布式框架Whale

简介&#xff1a; 最近&#xff0c;阿里云PAI团队和达摩院智能计算实验室一起发布“低碳版”巨模型M6&#xff0c;大幅降低万亿参数超大模型训练能耗。借助我们自研的Whale框架仅使用480卡GPU&#xff0c;即训练出了规模达人类神经元10倍的万亿参数多模态大模型M6&#xff0c;与…

居然之家:核心业务系统全面上云,采用PolarDB替代传统商业数据库

简介&#xff1a; 国内家居零售龙头企业居然之家完成7大核心业务系统全面上云工作&#xff0c;并实现ERP等核心业务系统从传统商业数据库向阿里云PolarDB云数据库的替换&#xff0c;助力业务系统整体处理能力提升50%以上&#xff0c;弹性能力提升3倍以上&#xff0c;大幅提升应…

c oracle实体模型,ADO.NET实体数据模型详细介绍

OleDbConnection&#xff0c;OracleConnection 或者SqlConnection这种连接&#xff0c;直接执行sql语句。现在的连接方式执行sql语句有了很大的不同&#xff0c;下面先看看简单的单表的增删改查操作&#xff0c;然后再看多表的关联查询&#xff0c;带参数查询等。一、ADO.NET E…

面向工业场景,如何实现绿色智能?

从瓦特的蒸汽机开始轰鸣&#xff0c;到爱迪生的电灯照亮黑暗&#xff0c;从埃尼阿克把0和1变成通用的语言&#xff0c;再到人工智能的无处不在。一次工业革命&#xff0c;会带来一次社会的演进&#xff0c;而每一次技术升级的背后&#xff0c;产业升级也几乎是必然。但产业发展…

云原生,开发者的黄金时代

简介&#xff1a; 如果说云是一种信仰&#xff0c;那么云原生就是一种态度&#xff0c;时代呼唤人人都应成为云原生开发者。 作者 | 丁宇&#xff08;叔同&#xff09;&#xff0c;阿里巴巴研究员&#xff0c;阿里云云原生应用平台负责人 对开发者而言&#xff0c;这是一个最…

如何玩转 WebGL 并行计算

简介&#xff1a; 如今在 Web 端使用 WebGL 进行高性能计算已有不少实践&#xff0c;例如在端智能领域中的 tensorflow.js&#xff0c;再比如可视化领域中的 Stardust.js。 作者 | 沧东 来源 | 阿里技术公众号 如今在 Web 端使用 WebGL 进行高性能计算已有不少实践&#xff0c…

数字孪生+交通,到底有啥用?

作者 | 小枣君来源 | 鲜枣课堂这些年来&#xff0c;信息技术的发展有了明显变化。以云计算、大数据、人工智能为代表的算力技术演进&#xff0c;以及以全光网络、4G/5G、Wi-Fi 6为代表的联接力技术飞跃&#xff0c;使得人们对数字技术提出了更高的期望。人们希望在信息化的基础…

万物智联时代的终端智能「管家」 重磅升级:混合云IoT一体机

简介&#xff1a; 「混合云IoT一体机」边缘部署、开箱即用、安全稳定、智管易用&#xff0c;通过定制软件和硬件相结合&#xff0c;预先定制、集成、测试和优化&#xff0c;实现快速部署和远程运维&#xff0c;并提升后续系统可用性和运维效率&#xff0c;是万物互联时代企业数…

今天来聊聊 Redis 的主从复制

作者 | 阿Q来源 | 阿Q说代码今天我们就从配置文件、设计原理、面试真题三个方面来聊一聊 Redis 的主从复制。在 Redis 复制的基础上&#xff0c;使用和配置主从复制非常简单&#xff0c;能使得从 Redis 服务器&#xff08;下文称 replica&#xff09;能精确的复制主 Redis 服务…

基于英特尔® 优化分析包(OAP)的 Spark 性能优化方案

简介&#xff1a; Spark SQL 作为 Spark 用来处理结构化数据的一个基本模块&#xff0c;已经成为多数企业构建大数据应用的重要选择。但是&#xff0c;在大规模连接&#xff08;Join&#xff09;、聚合&#xff08;Aggregate&#xff09;等工作负载下&#xff0c;Spark 性能会面…

表格长度_知道你的成绩单是怎么打印的吗?超长Excel表格1页打印,拯救A4纸

中小学的成绩单&#xff0c;红色的一张榜真实的魔鬼&#xff01;每次都得瞄半小时才找得到自己的全部科目成绩&#xff0c;不知道是不是为了节省A4纸~到了大学我才知道A4纸的珍贵&#xff0c;字小算什么&#xff0c;打印论文恨不得双面打印。要是能八号字打印更好了~到了工作的…

苹果电脑上使用linux环境变量,mac系统下修改环境变量

苹果电脑使用率越来越高&#xff0c;在mac系统下研发&#xff0c;性能要比在windows下快不少&#xff0c;既然要开发&#xff0c;免不了要配置环境变量.下面是学习啦小编收集整理的mac系统下修改环境变量&#xff0c;希望对大家有帮助~~mac系统下修改环境变量的方法工具/原料os…

提升代码质量的方法:领域模型、设计原则、设计模式

简介&#xff1a; 我们可以列举出非常多质量差的代码的表现现象&#xff0c;其中最影响代码质量的两个表现是命名名不副实、逻辑可扩展性差&#xff0c;当一个新人阅读代码时&#xff0c;有时发现方法命名与实际逻辑对不上&#xff0c;这就让人感到非常疑惑&#xff0c;这种现象…

SphereEx 完成近千万美元 Pre-A 轮融资,连接企业数据与应用,构建新一代数据库生态引擎

2022年1月4日&#xff0c;创新型数据库基础软件提供商 SphereEx 宣布完成近千万美元 Pre-A 轮融资&#xff0c;本轮融资由嘉御资本领投&#xff0c;红杉中国种子基金、初心资本、指数创投跟投。指数资本担任独家财务顾问。这是继 2021 年 5 月份以来&#xff0c;SphereEx 完成的…

Javascript 机器学习的四个层次

简介&#xff1a; Atwood定律说&#xff0c;凡是可以用Javascript实现的应用&#xff0c;最终都会用Javascript实现掉。作为最热门的机器学习领域&#xff0c;服务端是Python的主场&#xff0c;但是到了手机端呢&#xff1f;Android和iOS里默认都没有Python。但是有浏览器的地方…