Transaction 那点事儿,Spring事务管理

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。

Transaction 也就是所谓的事务了,通俗理解就是一件事情。从小,父母就教育我们,做事情要有始有终,不能半途而废。 事务也是这样,不能做一般就不做了,要么做完,要么就不做。也就是说,事务必须是一个不可分割的整体,就像我们在化学课里学到的原子,原子是构成物质的最小单位。于是,人们就归纳出事务的第一个特性:原子性(Atomicity)。我靠,一点都不神秘嘛。

特别是在数据库领域,事务是一个非常重要的概念,除了原子性以外,它还有一个极其重要的特性,那就是:一致性(Consistency)。也就是说,执行完数据库操作后,数据不会被破坏。打个比方,如果从 A 账户转账到 B 账户,不可能因为 A 账户扣了钱,而 B 账户没有加钱吧。如果出现了这类事情,您一定会非常气愤,什么 diao 银行啊!

当我们编写了一条 update 语句,提交到数据库的一刹那间,有可能别人也提交了一条 delete 语句到数据库中。也许我们都是对同一条记录进行操作,可以想象,如果不稍加控制,就会出大麻烦来。我们必须保证数据库操作之间是“隔离”的(线程之间有时也要做到隔离),彼此之间没有任何干扰。这就是:隔离性(Isolation)。要想真正的做到操作之间完全没有任何干扰是很难的,于是乎,每天上班打酱油的数据库专家们,开始动脑筋了,“我们要制定一个规范,让各个数据库厂商都支持我们的规范!”,这个规范就是:事务隔离级别(Transaction Isolation Level)。能定义出这样牛逼的规范真的挺不容易的,其实说白了就四个级别:

  1. READ_UNCOMMITTED
  2. READ_COMMITTED
  3. REPEATABLE_READ
  4. SERIALIZABLE

千万不要去翻译,那只是一个代号而已。从上往下,级别越来越高,并发性越来越差,安全性越来越高,反之则反。

当我们执行一条 insert 语句后,数据库必须要保证有一条数据永久地存放在磁盘中,这个也算事务的一条特性, 它就是:持久性(Durability)。

归纳一下,以上一共提到了事务的 4 条特性,把它们的英文单词首字母合起来就是:ACID,这个就是传说中的“事务 ACID 特性”!

真的是非常牛逼的特性啊!这 4 条特性,是事务管理的基石,一定要透彻理解。此外还要明确,这四个家伙当中,谁才是老大?

其实想想也就清楚了:原子性是基础,隔离性是手段,持久性是目的,真正的老大就是一致性。数据不一致了,就相当于“江湖乱套了,流氓戴胸罩”。所以说,这三个小弟都是跟着“一致性”这个老大混,为他全心全意服务。

这四个家伙当中,其实最难理解的反倒不是一致性,而是隔离性。因为它是保证一致性的重要手段,是工具,使用它不能有半点差池,否则后果自负!怪不得数据库行业专家们都要来研究所谓的事务隔离级别了。其实,定义这四个级别就是为了解决数据在高并发下所产生的问题,那又有哪些问题呢?

  1. Dirty Read(脏读)
  2. Unrepeatable Read(不可重复读)
  3. Phantom Read(幻读)

首先看看“脏读”,看到“脏”这个字,我就想到了恶心、肮脏。数据怎么可能脏呢?其实也就是我们经常说的“垃圾数据”了。比如说,有两个事务,它们在并发执行(也就是竞争)。看看以下这个表格,您一定会明白我在说什么:

 

时间事务 A(存款)事务 B(取款)
T1开始事务 
T2 开始事务
T3 查询余额(1000 元)
T4 取出 1000 元(余额 0 元)
T5查询余额(0 元) 
T6 撤销事务(余额恢复为 1000 元)
T7存入 500 元(余额 500 元) 
T8提交事务 


余额应该为 1500 元才对!请看 T5 时间点,事务 A 此时查询余额为 0 元,这个数据就是脏数据,它是事务 B 造成的,明显事务没有进行隔离,渗过来了,乱套了。

所以脏读这件事情是非常要不得的,一定要解决掉!让事务之间隔离起来才是硬道理。

那第 2 条,不可重复读又怎么解释呢?还是用类似的例子来说明:

 

时间事务 A(存款)事务 B(取款)
T1开始事务 
T2 开始事务
T3 查询余额(1000 元)
T4查询余额(1000 元) 
T5 取出 1000 元(余额 0 元)
T6 提交事务
T7查询余额(0 元) 


事务 A 其实除了查询了两次以外,其他什么事情都没有做,结果钱就从 1000 变成 0 了,这就是重复读了。可想而知,这是别人干的,不是我干的。其实这样也是合理的,毕竟事务 B 提交了事务,数据库将结果进行了持久化,所以事务 A 再次读取自然就发生了变化。

这种现象基本上是可以理解的,但在有些变态的场景下却是不允许的。毕竟这种现象也是事务之间没有隔离所造成的,但我们对于这种问题,似乎可以忽略。

最后一条,幻读。我去!Phantom 这个单词不就是“幽灵、鬼魂”吗?刚看到这个单词时,真的把我的小弟弟都给惊呆了。怪不得这里要翻译成“幻读”了,总不能翻译成“幽灵读”、“鬼魂读”吧。其实意义就是鬼在读,不是人在读,或者说搞不清楚为什么,它就变了,很晕,真的很晕。还是用一个示例来说话吧:

时间事务 A(统计总存款)事务 B(存款)
T1开始事务 
T2 开始事务
T3统计总存款(10000 元) 
T4 存入 100 元
T5 提交事务
T6统计总存款(10100 元) 


银行工作人员,每次统计总存款,都看到不一样的结果。不过这也确实也挺正常的,总存款增多了,肯定是这个时候有人在存钱。但是如果银行系统真的这样设计,那算是玩完了。这同样也是事务没有隔离所造成的,但对于大多数应用系统而言,这似乎也是正常的,可以理解,也是允许的。银行里那些恶心的那些系统,要求非常严密,统计的时候,甚至会将所有的其他操作给隔离开,这种隔离级别就算非常高了(估计要到 SERIALIZABLE 级别了)。

归纳一下,以上提到了事务并发所引起的跟读取数据有关的问题,各用一句话来描述一下:

  1. 脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又做了其他操作。
  2. 不可重复读:事务 A 读取了事务 B 已提交的更改数据。
  3. 幻读:事务 A 读取了事务 B 已提交的新增数据。

第一条是坚决抵制的,后两条在大多数情况下可不作考虑。

这就是为什么必须要有事务隔离级别这个东西了,它就像一面墙一样,隔离不同的事务。看下面这个表格,您就清楚了不同的事务隔离级别能处理怎样的事务并发问题:

 

事务隔离级别脏读不可重复读幻读
READ_UNCOMMITTED允许允许允许
READ_COMMITTED禁止允许允许
REPEATABLE_READ禁止禁止允许
SERIALIZABLE禁止禁止禁止


根据您的实际需求,再参考这张表,最后确定事务隔离级别,应该不再是一件难事了。

JDBC 也提供了这四类事务隔离级别,但默认事务隔离级别对不同数据库产品而言,却是不一样的。我们熟知的 MySQL 数据库的默认事务隔离级别就是 READ_COMMITTED,Oracle、SQL Server、DB2等都有有自己的默认值。我认为 READ_COMMITTED 已经可以解决绝大多数问题了,其他的就具体情况具体分析吧。

若对其他数据库的默认事务隔离级别不太清楚,可以使用以下代码来获取:


DatabaseMetaData meta = DBUtil.getDataSource().getConnection().getMetaData();
int defaultIsolation = meta.getDefaultTransactionIsolation();

提示:在 java.sql.Connection 类中可查看所有的隔离级别。

我们知道 JDBC 只是连接 Java 程序与数据库的桥梁而已,那么数据库又是怎样隔离事务的呢?其实它就是“锁”这个东西。当插入数据时,就锁定表,这叫“锁表”;当更新数据时,就锁定行,这叫“锁行”。当然这个已经超出了我们今天讨论的范围,所以还是留点空间给我们的 DBA 同学吧,免得他没啥好写的了。

除了 JDBC 给我们提供的事务隔离级别这种解决方案以外,还有哪些解决方案可以完善事务管理功能呢?

不妨看看 Spring 的解决方案吧,其实它是对 JDBC 的一个补充或扩展。它提供了一个非常重要的功能,就是:事务传播行为(Transaction Propagation Behavior)。

确实够牛逼的,Spring 一下子就提供了 7 种事务传播行为,这 7 种行为一出现,真的是亮瞎了我的狗眼!

  1. PROPAGATION_REQUIRED
  2. RROPAGATION_REQUIRES_NEW
  3. PROPAGATION_NESTED
  4. PROPAGATION_SUPPORTS
  5. PROPAGATION_NOT_SUPPORTED
  6. PROPAGATION_NEVER
  7. PROPAGATION_MANDATORY

看了 Spring 参考手册之后,更是晕了,这到底是在干嘛?

首先要明确的是,事务是从哪里来?传播到哪里去?答案是,从方法 A 传播到方法 B。Spring 解决的只是方法之间的事务传播,那情况就多了,比如:

  1. 方法 A 有事务,方法 B 也有事务。
  2. 方法 A 有事务,方法 B 没有事务。
  3. 方法 A 没有事务,方法 B 有事务。
  4. 方法 A 没有事务,方法 B 也没有事务。 

这样就是 4 种了,还有 3 种特殊情况。还是用我的 Style 给大家做一个分析吧:

假设事务从方法 A 传播到方法 B,您需要面对方法 B,问自己一个问题:

方法 A 有事务吗?

  1. 如果没有,就新建一个事务;如果有,就加入当前事务。这就是 PROPAGATION_REQUIRED,它也是 Spring 提供的默认事务传播行为,适合绝大多数情况。
  2. 如果没有,就新建一个事务;如果有,就将当前事务挂起。这就是 RROPAGATION_REQUIRES_NEW,意思就是创建了一个新事务,它和原来的事务没有任何关系了。
  3. 如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。这就是 PROPAGATION_NESTED,也就是传说中的“嵌套事务”了,所嵌套的子事务与主事务之间是有关联的(当主事务提交或回滚,子事务也会提交或回滚)。
  4. 如果没有,就以非事务方式执行;如果有,就使用当前事务。这就是 PROPAGATION_SUPPORTS,这种方式非常随意,没有就没有,有就有,有点无所谓的态度,反正我是支持你的。
  5. 如果没有,就以非事务方式执行;如果有,就将当前事务挂起。这就是 PROPAGATION_NOT_SUPPORTED,这种方式非常强硬,没有就没有,有我也不支持你,把你挂起来,不鸟你。
  6. 如果没有,就以非事务方式执行;如果有,就抛出异常。这就是 PROPAGATION_NEVER,这种方式更猛,没有就没有,有了反而报错,确实够牛的,它说:我从不支持事务!
  7. 如果没有,就抛出异常;如果有,就使用当前事务。这就是 PROPAGATION_MANDATORY,这种方式可以说是牛逼中的牛逼了,没有事务直接就报错,确实够狠的,它说:我必须要有事务!

看到我上面这段解释,小伙伴们是否已经感受到,被打通任督二脉的感觉?多读几遍,体会一下,就是您自己的东西了。

需要注意的是 PROPAGATION_NESTED,不要被它的名字所欺骗,Nested(嵌套),所以凡是在类似方法 A 调用方法 B 的时候,在方法 B 上使用了这种事务传播行为,如果您真的那样做了,那您就错了。因为您错误地以为 PROPAGATION_NESTED 就是为方法嵌套调用而准备的,其实默认的 PROPAGATION_REQUIRED 就可以帮助您,做您想要做的事情了。

Spring 给我们带来了事务传播行为,这确实是一个非常强大而又实用的功能。除此以外,也提供了一些小的附加功能,比如:

  1. 事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,所以故意给事务设置一个最大时常,如果超过了,就回滚事务。
  2. 只读事务(Readonly Transaction):为了忽略那些不需要事务的方法,比如读取数据,这样可以有效地提高一些性能。

最后,推荐大家使用 Spring 的注解式事务配置,而放弃 XML 式事务配置。因为注解实在是太优雅了,当然这一切都取决于您自身的情况了。

在 Spring 配置文件中使用:

...
<tx:annotation-driven />
...

在需要事务的方法上使用:


@Transactional
public void xxx() {...
}

可在 @Transactional 注解中设置:事务隔离级别、事务传播行为、事务超时时间、是否只读事务。

简直是太完美了,太优雅了!

最后,有必要对本文的内容做一个总结,免费赠送一张高清无码思维导图:

期盼您的对本文的评价!感谢您的关注与支持!

 

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

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

相关文章

8.类定义、属性、初始化和析构

类定义 类 是一个独立存放变量(属性/方法)的空间 封装&#xff1a; 类可以把各种对象组织在一起&#xff0c;作为类的属性&#xff0c;通过 . (点)运算符来调用类中封装好的对象 属性&#xff1a; 变量在类中称为属性&#xff0c;但是类中的属性不仅仅只包含变量&#x…

网络爬虫--13.数据提取之JSON与JsonPATH

文章目录一. 前言二. JSON三. json.loads()四. json.dumps()五. json.dump()六. json.load()七. JsonPath八. JsonPath与XPath语法对比九. 案例分析一. 前言 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式&#xff0c;它使得人们很容易的进行阅读和编写。同时…

创建型模式二:工厂方法模式

1. 工厂模式介绍 工厂模式&#xff08;Factory Pattern&#xff09;的意义就跟它的名字一样&#xff0c;在面向对象程序设计中&#xff0c;工厂通常是一个用来创建其他对象的对象。工厂模式根据不同的参数来实现不同的分配方案和创建对象。 在工厂模式中&#xff0c;我们在创建…

spring 的4种事务管理(1种编程式+3种声明式)

见&#xff1a;http://blog.csdn.net/sinat_25926481/article/details/48208619 Spring的4种事务管理&#xff08;1种编程式事务三种声明事务&#xff09; 一、Spring事务的介绍 二、编程式事务xml的配置 注入后直接在service层调用模板的方法使用 三、基于AOP方式的声明式事务…

如何创造出更优秀的用户体验?

对于互联网公司来说&#xff0c;用户体验起到至关重要的作用&#xff0c;能否给用户留下深刻的印象&#xff1b;开发出的产品是否实用、易用&#xff1f;等等这些都是开发者必将思考的话题。当有用性一样的时候&#xff0c;大家的竞争重点就是易用性了&#xff0c;这就是互联网…

GitHub(从安装到使用)

一、安装Git for Windows&#xff08;又名msysgit&#xff09; 下载地址: https://git-for-windows.github.io/ 在官方下载完后&#xff0c;安装到Windows Explorer integration的时候&#xff0c;将选项中将“Git Bash here”和“Git GUI here”打对勾。 然后就一直next直到Fi…

Spring事务配置的五种方式和spring里面事务的传播属性和事务隔离级别、不可重复读与幻读的区别

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 spring事务配置的五种方式 前段时间对Spring的事务配置做了比较深入的研究&#xff0c;在此之间对Spring的事务配置虽说也配置过&#x…

Google编程之夏入围项目公布

Google编程之夏&#xff08;Google Summer of Code&#xff09;&#xff0c;是由Google公司所主办的年度程式设计比赛&#xff0c;第一届从2005年开始。“Summer of Code”之名取自1967年的“夏日之爱”&#xff08;Summer of Love&#xff09;风潮。比赛的主要目的是鼓励学生参…

微软:软件帝王的复兴之路

可以说在过去的两个月IT界所发生的一切都非同寻常&#xff0c;乔布斯辞职了&#xff0c;Google把Motorola并购了&#xff0c;微软炫了一下Windows 8&#xff0c;还宣布开始用ARM了&#xff0c;Google开始和英特尔合作了&#xff0c;AT&T与T-Mobile的并购也在紧密锣鼓进行中…

Eclipse GBK批量转UTF-8插件(转)

最近需要把Android项目转Android Studio&#xff0c;由于之前是eclipse开发&#xff0c;而且坑爹的是编码还是GBK的&#xff0c;转到Android Studio中文都是乱码&#xff0c;如果一个文件一个文件ctrlc的话&#xff0c;想想就累&#xff0c;几经Google&#xff0c;发现一个很好…

网络爬虫--15.【糗事百科实战】多线程实现

文章目录一. Queue&#xff08;队列对象&#xff09;二. 多线程示意图三. 代码示例一. Queue&#xff08;队列对象&#xff09; Queue是python中的标准库&#xff0c;可以直接import Queue引用;队列是线程间最常用的交换数据的形式 python下多线程的思考 对于资源&#xff0…

网络爬虫--16.BeautifulSoup4

文章目录一. BeautifulSoup4二. 解析实例三. 四大对象种类1. Tag2. NavigableString3. BeautifulSoup4. Comment四. 遍历文档树1.直接子节点 &#xff1a;.contents .children 属性1). .contents2). .children2. 所有子孙节点: .descendants 属性3. 节点内容: .string 属性五. …

【LA3415 训练指南】保守的老师 【二分图最大独立集,最小割】

题意 Frank是一个思想有些保守的高中老师。有一次&#xff0c;他需要带一些学生出去旅行&#xff0c;但又怕其中一些学生在旅行中萌生爱意。为了降低这种事情发生的概率&#xff0c;他决定确保带出去的任意两个学生至少要满足下面四条中的一条。 1.身高相差大于40厘米 2.性别相…

行车记录仪稳定方案:TC358778XBG:RGB转MIPI DSI芯片,M-Star标配IC

原厂&#xff1a;Toshiba型号&#xff1a;TC358778XBG功能&#xff1a;TC358778XBG是一颗将RGB信号转换成MIPI DSI的芯片&#xff0c;最高分辨率支持到1920x1200&#xff0c;其应用图如下&#xff1a;产品特征&#xff1a;MIPI接口&#xff1a;&#xff08;1&#xff09;、支持…

java.sql.SQLException: 无法转换为内部表示之解决

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 这个错是因为 数据库中字段类型和程序中该字段类型不一致。 比如程序将某字段当做Integer类型&#xff0c; 而数据库存储又使用另外一…

网络爬虫--17.【BeautifuSoup4实战】爬取腾讯社招

文章目录一.要求二.代码示例一.要求 以腾讯社招页面来做演示&#xff1a;http://hr.tencent.com/position.php?&start10#a 使用BeautifuSoup4解析器&#xff0c;将招聘网页上的职位名称、职位类别、招聘人数、工作地点、发布时间&#xff0c;以及每个职位详情的点击链接…

彻底搞清楚Android中的 Attr

版权声明&#xff1a;本文为sydMobile原创文章&#xff0c;转载请务必注明出处&#xff01; https://blog.csdn.net/sydMobile/article/details/79978187 相信这个词对于Android开发者来说十分熟悉了&#xff0c;那么你对他到底有多了解呢&#xff1f; 回忆起我刚开始接触Andr…

D. Relatively Prime Graph

Lets call an undirected graph G(V,E)G(V,E) relatively prime if and only if for each edge (v,u)∈E(v,u)∈E GCD(v,u)1GCD(v,u)1 (the greatest common divisor of vv and uu is 11). If there is no edge between some pair of vertices vv and uu then the value of GC…

网络爬虫--19.【Scrapy-Redis实战】分布式爬虫爬取房天下--环境准备

文章目录0. 思路一. 虚拟机Ubuntu0中安装Redis二. 虚拟机Ubuntu1中安装Redis三. Windows服务器上安装Redis四. 安装cmder五. 安装RedisDesktopManager六. 修改Windows中的配置文件redis.windows.conf七. Ubuntu连接Windows上 的Redis服务器-----------------------------------…

tkinter中scale拖拉改变值控件(十一)

scale拖拉改变值控件 使用户通过拖拽改变值 简单的实现&#xff1a; 1 import tkinter2 3 wuya tkinter.Tk() 4 wuya.title("wuya") 5 wuya.geometry("300x2001020") 6 7 8 # 创建对象 9 scale1 tkinter.Scale(wuya, from_0, to100) 10 scale1.pac…