Spring 事务传播行为
事务隔离级别
事务隔离级别指一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,可能发生以下问题:
问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务未提交的更新数据,所谓脏读,就是指事务 A 读到了事务 B 还没有提交的数据,比如银行取钱,事务 A 开启事务,此时切换到事务 B,事务 B 开启事务-->取走 100 元,此时切换回事务 A,事务 A 读取的肯定是数据库里面的原始数据,因为事务 B 取走了 100 块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读 |
幻读 | 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。 |
不可重复读 | 在一个事务里面的操作中发现了未被操作的数据 比方说在同一个事务中先后执行两条一模一样的 select 语句,期间在此次事务中没有执行过任何 DDL 语句,但先后得到的结果不一致,这就是不可重复读 |
Spring 支持的隔离级别
隔离级别 | 描述 |
---|---|
DEFAULT | 使用数据库本身使用的隔离级别<br> ORACLE(读已提交) MySQL(可重复读) |
READ_UNCOMITTED | 读未提交(脏读)最低的隔离级别,一切皆有可能。 |
READ_COMMITED | 读已提交,ORACLE 默认隔离级别,有幻读以及不可重复读风险 |
REPEATABLE_READ | 可重复读,解决不可重复读的隔离级别,但还是有幻读风险。 |
SERLALIZABLE | 串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了 |
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 | --- |
---|---|---|---|---|---|
READ_UNCOMITTED(读未提交) | 否 | 是 | 是 | 否 | |
READ_COMMITED(读已提交) | 否 | 是 | 是 | 否 | |
REPEATABLE_READ (可重复读) | 否 | 否 | 是 | 否 | |
SERLALIZABLE (串行读) | 否 | 否 | 否 | 否 | 是 |
事务传播行为
Spring 7 个事务传播行为
事务行为 | 说明 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,假设当前没有事务。就新建一个事务 |
PROPAGATION_SUPPORTS | 支持当前事务,假设当前没有事务,就以非事务方式运行 |
PROPAGATION_MANDATORY | 支持当前事务,假设当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,假设当前存在事务。把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式运行操作。假设当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式运行,假设当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。 |
代码说明
ServiceA {
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
void methodB() {
}
}
PROPAGATION_REQUIRED
假如当前正要运行的事务不在另外一个事务里,那么就起一个新的事务.ServiceB.methodB 的事务级别定义 PROPAGATION_REQUIRED, 那么因为执行 ServiceA.methodA 的时候,ServiceA.methodA 已经起了事务。这时调用 ServiceB.methodB,ServiceB.methodB 看到自己已经执行在 ServiceA.methodA 的事务内部。就不再起新的事务。而假如 ServiceA.methodA 执行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在 ServiceA.methodA 或者在 ServiceB.methodB 内的不论什么地方出现异常。事务都会被回滚。即使 ServiceB.methodB 的事务已经被提交,可是 ServiceA.methodA 在接下来 fail 要回滚,ServiceB.methodB 也要回滚
PROPAGATION_SUPPORTS
假设当前在事务中。即以事务的形式执行。假设当前不在一个事务中,那么就以非事务的形式执行
PROPAGATION_MANDATORY
必须在一个事务中执行。也就是说,他仅仅能被一个父事务调用。否则,他就要抛出异常
PROPAGATION_REQUIRES_NEW
比方我们设计 ServiceA.methodA 的事务级别为 PROPAGATION_REQUIRED,ServiceB.methodB 的事务级别为 PROPAGATION_REQUIRES_NEW。那么当运行到 ServiceB.methodB 的时候,ServiceA.methodA 所在的事务就会挂起。ServiceB.methodB 会起一个新的事务。等待 ServiceB.methodB 的事务完毕以后,他才继续运行。 与 PROPAGATION_REQUIRED 的事务差别在于事务的回滚程度了。由于 ServiceB.methodB 是新起一个事务,那么就是存在两个不同的事务。假设 ServiceB.methodB 已经提交,那么 ServiceA.methodA 失败回滚。ServiceB.methodB 是不会回滚的。假设 ServiceB.methodB 失败回滚,假设他抛出的异常被 ServiceA.methodA 捕获,ServiceA.methodA 事务仍然可能提交。
PROPAGATION_NOT_SUPPORTED
当前不支持事务。比方 ServiceA.methodA 的事务级别是 PROPAGATION_REQUIRED 。而 ServiceB.methodB 的事务级别是 PROPAGATION_NOT_SUPPORTED ,那么当执行到 ServiceB.methodB 时。ServiceA.methodA 的事务挂起。而他以非事务的状态执行完,再继续 ServiceA.methodA 的事务。
PROPAGATION_NEVER
不能在事务中执行。如果 ServiceA.methodA 的事务级别是 PROPAGATION_REQUIRED。 而 ServiceB.methodB 的事务级别是 PROPAGATION_NEVER ,那么 ServiceB.methodB 就要抛出异常了。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。