lvgo

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

这将会是你编程生涯很有意义的10分钟

背景

反对枯燥的技术学习,拒绝从入门到放弃,从删库到跑路。
拒绝!大声跟我念!拒绝!

啥是事务

事物指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败
同生共死

事务的四个特性 ACID

原子性(Atomicity):事物是一个不可分割的工作单位,事物中的操作要么都发生,要么都不发生

一致性(Consistency):事物前后数据的完整性必须保持一致

隔离性(Isolation):指多个用户并发访问数据库时,一个用户的事物不能被其他用户的事物所干扰,多个并发事物之间数据要相互隔离。

持久性(Durability):一个事物一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响。

事务的7种传播行为

这里我看到好多官方或者博文写的基本都是差不多. 如果我会用的话, 我可能不会去翻书翻博文找答案. 那既然翻书的和找答案的都是不会用的. 那怎么能让寻求答案的人, 一眼就看到自己想要的东西呢?

首先7种传播行为分别为:

名字有可能和大家看到其他的文章内容不一样. 个人理解. 不影响阅读

  1. 默认事务 REQUIRED(开启事务后不做任何传播行为说明的)
  2. 新启事务 REQUIRES_NEW
  3. 嵌套事务 NESTED
  4. 依赖事务 SUPPORTS
  5. 非事务 NOT_SUPPORTED
  6. 必须非事务 NEVER
  7. 必须以事务 MANDATORY

那这7个分别对应的是什么情景, 我们来用最直白, 最简单的话来说明一下.

1. 默认事务 REQUIRED

顾名思义, 当你对某个方法开启了事务, 但又不去指定它的传播行为时, 此时的事务管理会默认分配给他一个事务. 那就是调用者如果有事务便加入其中, 否则自身新启事务. 什么意思

你去小李家玩, 玩累了, 想吃饭, 如果小李家可以吃饭, 那就在小李家一起吃, 否则你就自己吃.

2. 新启事务 REQUIRES_NEW

与默认事务中的第二种情况相同. 只不过, 此时无论小李家可不可以吃饭, 你都要自己吃, 不过还有一种情况, 就是如果调用者存在事务, 那该事务需要挂起.也就是说. 小李家如果可以吃饭, 那他们家人要等你吃完以后才能吃, 相当于暂停. 但是大家要切记, 这段时间只是时间暂停, 资源是在继续使用的. 也就是说, 外层事务挂起时, 数据库连接是一直持有状态. 所以你吃饭要快点. 不然等你吃的太久, 小李家其他人一直在等他们的餐桌吃饭可是要一直等下去的. 此时说的是事务挂起的情况

3. 嵌套事务 NESTED

厉害的来了, 这个事务比较特殊, 适用的场景大家根据我后面的说明自行 决断;
当前存在事务, 加入到这个事务中作为一个附属事务. 如果不存在, 则以默认事务执行, 其实也就是以新启事务执行;

解释一下附属事务的情况是什么意思. 附属, 归属,具体表现为我们的县城, 归属市区管辖, 但自己又独立. 当市级A()派遣任务给县城B(), 与它一同协作完成. 如果县城B()没有预期完成任务,我们说的抛出异常, 那市级A()会继续执行该市的其他任务. 如果市级预期完成, 那么县城的任务失败, 市级负责的部分任务完成. 此时事务A()提交. 县城事务B()回滚.
但, 如果县城B()按期完成任务, 市级A()任务没有完成. 那县城B()的事务要同A()一起回滚.

// 独立事务
a() {
	// 嵌套事务
	b();
}

a调用b , a 为独立事务 b 为嵌套事务
当执行到方法b时, a事务被挂起. 方法 b 无论成功失败, a继续执行
a执行成功 a与b 事务提交
a执行失败 无论b事务是否完成 与a一同回滚

嵌套事务只影响本身事务, 不能影响外层事务. 同时受外层事务影响
a可以影响b , 但b不能影响a

顺便提一点 如果b为默认事务, 此时 b 如果出现异常, 整个事务执行回滚, 不能对b做异常捕获不抛出异常. 否则会出现事务提交异常, 因为此时 a 与 b 同处一个事务中, 要么同时提交, 要么同时回滚.


上面讲的就是我们最常用的三种传播行为, 下面再看看其他4种不常用的

4. 依赖事务 SUPPORTS

这种事务情况为, 如果调用者有事务我就加入, 没有我就以非事务执行
小李家有饭我就吃, 没有我就不吃了

5. 非事务 NOT_SUPPORTED

如果调用者有事务我就把他挂起, 然后以非事务执行
小李家有饭我就让他暂停, 然后我做我的事,做完后恢复挂起事务继续执行

6. 必须非事务 NEVER

如果调用者有事务我就抛出异常. 不再继续执行
小李家有饭我就报警, 谁都别想动

7. 必须以事务 MANDATORY

如果调用者没有事务我就抛出异常. 不再继续执行
小李家没有饭我就报警, 谁都别想动


接下来你应该知道的

说了基本概念, 我觉得既然是最简单的事务学习, 那一定是要跟大家讲怎么使用事务.具体怎么去配置事务大家随便百度吧. 就不赘述了.

当你将你的方法配置好事务后. 有几个基础的东西你一定要知道, 当然现在用的事务都是spring的事务.

1. 同方法内, 被调用的事务不会生效

举例

class ServiceA (
	// 方法 A() 配有独立事务
	fun A(){
		// 调用 方法B(), 记笔记. 此时 B 的独立事务不生效
		B();
	}
	// 方法 B() 配有独立事务
	fun B(){

	}
)

该问题比较好理解. 原因是由于spring的事务管理是基于 spring 的 AOP 实现的. 而方法增强需要通过调用时执行切点方法才能实现增强. 所以此时的方法B就是正常的一段方法加入到了方法A中, 与 A同事务.

2. 默认事务的异常处理*[重点]

我现在有以下几个方法

class ServiceA (
	// 方法 A() 配有默认事务
	fun A(){
		// 调用 方法B()
		ServiceB.B();
	}
)

class ServiceB (
	// 方法 B() 配有默认事务
	fun B(){
		// 调用 方法C()
		try{
			ServiceC.C();
		} catch(){}
	}
	
)

class ServiceC (
	// 方法 C() 配有默认事务
	fun C(){
		
	}
)

我将Service类A B C 都增加了事务. 但没配置具体事务传播行为. 所以为默认事务.
默认事务为: 谁用谁管. 不管我就自己管自己

此时的情况为, A在调用的时候没有事务, 所以他自己新启了事务, 当它调用B的时候, B发现A存在独立事务, 那么B加入到了A的事务中, 同样 B在调用C的时候已经加入到了A的事务中, 那么C在执行的时候加入到了A的事务中.

调用者有事务, 我加入,
调用者没有, 我新启.

此时情况. 正常运行, 为独立事务没有问题.

但当方法 C()出现异常时. 被方法 B()捕获并未抛出.
当方法执行结束后, 会出现
Transaction has been rolled back because it has been marked as rollback

原因为上面提到的

原子性(Atomicity):事物是一个不可分割的工作单位,事物中的操作要么都发生,要么都不发生

方法C()不发生, 其他也不能继续发生 . 所以当C()出现问题时, B() 把它包庇了下来. 当时A()不知道, 还继续做提交, 说我们的事务都提交了. 其实C()回滚了. 最后来事情暴露了. 程序执行中断. 这个锅. B 必须背. 因为当它捕获了异常后, 应将异常继续抛出. 让程序正常中断. 不然就是意外退出.

切记 – 知识点, 记笔记

posted on 2019-09-12 14:52  星尘的一个朋友  阅读(105)  评论(0编辑  收藏  举报