1. 脏读、不可重复读、幻读
1.1. 脏读
事务A
修改了数据项a
, 但没提交。事务B
读到了这个修改后的结果,即事务B
出现脏读。
1.2. 不可重复读
事务A
在一个事务内,多次读同一个数据a
,读到的结果前后不一样
如,事务
A
多次读a
,然后事务B
修改了a
并提交,然后事务A
读出来的数据是修改后的a
1.3. 幻读
指当事务不是独立执行时发生的一种现象。
如事务A
对某一范围R
的数据进行读取/修改得到结果集Result
,然后事务B
在范围R
中插入/删除数据然后提交,这时候结果集Result
改变了(被A
读到了,好像产生幻觉一样)
2. 隔离级别
2.1. ISOLATION_DEFAULT
默认,使用数据库默认隔离级别
2.2. ISOLATION_READ_UNCOMMITTED
最低隔离级别,可读到未提交数据。会产生脏读、不可重复读、幻读。
2.3. ISOLATION_READ_COMMITTED
提交读,只能读到事务被提交的数据。无脏读,但有不可重复读、幻读。
2.4. ISOLATION_REPEATABLE_READ
可重复读,保证了可重复读。可以防止脏读、不可重复读,但可能有幻读(MySQL的InnoDB采用间隙锁,防止间隙插入数据,以防止幻读)
2.5. ISOLATION_SERIALIZABLE
最高级别,事务被串行执行,可防止脏读、不可重复读、幻读。
3. 事务传播级别
在类/方法中使用注解@Transactional(propagation=VALUE)
而实现事务时,Spring会创建一个代理放入IoC容器中进行管理(即AOP),只有调用这个代理才能使用事务!
所以要注意的是,以下创建的对象调用注解方法时,不会产生事务:
- 显式
new
一个对象后,调用该对象的@Transactional
方法 - 在该对象内部调用注解
@Transactional
方法
下面以ServiceA::methodA
调用ServiceB::methodB
为例以说明:
ServiceA::methodA
可能在事务环境中(也可能不在)ServiceB::methodB
分别被以下7个级别注解。
3.1. PROPAGATION_REQUIRED
- 若
ServiceA::methodA
在事务中,则ServiceB::methodB
和ServiceA::methodA
属于同一个事务运行 - 若
ServiceA::methodA
不在事务中,则ServiceB::methodB
新创建一个事务
3.2. PROPAGATION_SUPPORTS
- 若
ServiceA::methodA
在事务中,则ServiceB::methodB
和ServiceA::methodA
属于同一个事务运行 - 若
ServiceA::methodA
不在事务中,则ServiceB::methodB
也不以事务运行
3.3. PROPAGATION_MANDATORY
必须在一个事务中运行,即:
- 若
ServiceA::methodA
在事务中,则ServiceB::methodB
和ServiceA::methodA
属于同一个事务运行 - 若
ServiceA::methodA
不在事务中,则ServiceB::methodB
抛异常
3.4. PROPAGATION_REQUIRES_NEW
永远创建一个新的事务,且和外部的事务独立。
例如ServiceA::methodA
在事务环境下,首先它先要被挂起,等ServiceB::methodB
执行完后才继续执行:
ServiceB::methodB
成功提交,无论ServiceA::methodA
回滚与否,Service::methodB
不会被回滚ServiceB::methodB
失败回滚,抛出异常- 异常被
ServiceA::methodA
处理,则ServiceA::methodA
被提交 - 异常没被处理,
ServiceA::methodA
也被回滚
- 异常被
Log Service要用这个级别的事务传播级别
3.5. PROPAGATION_NOT_SUPPORTED
- 若
ServiceA::methodA
在事务中,则先挂起,然后以非事务方式执行ServiceB::methodB
,返回后再继续ServiceA::methodA
的事务 - 若
ServiceA::methodA
不在事务中,则ServiceB::methodB
也不以事务运行
3.6. PROPAGATION_NEVER
- 若
ServiceA::methodA
在事务中,ServiceB::methodB
调用抛异常 - 若
ServiceA::methodA
不在事务中,则ServiceB::methodB
也不以事务运行
3.7. PROPAGATION_NESTED
- 若
ServiceA::methodA
在事务中,它被挂起,ServiceB::methodB
创建子事务,等子事务完成后,才恢复执行自己事务- 若
ServiceB::methodB
成功提交,若ServiceA::methodA
失败回滚,则ServiceB::methodB
也回滚 - 若
ServiceB::methodB
失败回滚,抛出异常- 异常被
ServiceA::methodA
处理,则ServiceA::methodA
被提交 - 异常没被处理,
ServiceA::methodA
也被回滚
- 异常被
- 若
创建子事务(嵌套事务)时,父事务创建一个回滚点Save Point:
- 子事务失败回滚,将会回滚到Save Point,父事务会尝试其它操作,不会自动回滚
- 若父事务回滚,则子事务也回滚,子事务是父事务的一部分
- 子事务先于父事务提交
- 若
ServiceA::methodA
不在事务中,则ServiceB::methodB
也以PROPAGATION_REQUIRED
执行