Spring事务

Posted by keys961 on July 20, 2018

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::methodBServiceA::methodA属于同一个事务运行
  • ServiceA::methodA不在事务中,则ServiceB::methodB新创建一个事务

3.2. PROPAGATION_SUPPORTS

  • ServiceA::methodA在事务中,则ServiceB::methodBServiceA::methodA属于同一个事务运行
  • ServiceA::methodA不在事务中,则ServiceB::methodB也不以事务运行

3.3. PROPAGATION_MANDATORY

必须在一个事务中运行,即:

  • ServiceA::methodA在事务中,则ServiceB::methodBServiceA::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执行