Spring-事务

事务是什么?

事务是以可控的方式对数据资源进行访问的一组操作。

数据库事务的四个特性

数据库事务的四大特性统称为ACID,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

原子性(Atomicity)

原子性的本意是不可拆分,表示事务中包含的操作要么全部成功,要么全部失败回滚,不存在部分成功部分失败的场景。
如果全部成功则事务内的所有操作都将被应用到数据库,如果全部失败则不会对数据库有任何影响。

一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说数据在事务执行之前和执行之后必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

隔离性(Isolation)

数据库存在多个事务同时操作时,应该保证事务时间互相不干扰,操作互相不影响。
如果像java语言中通过同步锁线性的操作,隔离性的问题肯定可以解决,但是这样操作就导致事务都是串行操作,效率低下,因此可以通过适当的调整隔离性,也就是事务互相之间的可见性的程度,来更好的提高性能。这就是隔离性所牵扯到一个隔离级别的问题,本文后面的章节会介绍到。

持久性(Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,不会存在提交后,由于系统的原因导致操作的效果消失了。

Spring中使用事务的两种方式

编程式事务

编程式事务是利用Spring的AOP功能,在操作数据库的方法处做切入,其过程可以用伪代码写成:

1
2
3
4
5
6
7
8
9
10
11
12
aop方法{
//1、获取连接
//2、开启非自动提交
try{
//3、数据库增删改查
//4、提交事务
}catch(Exception e){
//5、回滚事务
}finally{
//6、释放资源
}
}

声明式事务

通过注解(@Transactional)的方式,告诉Spring哪些方法是事务方法,事务中的回滚,提交、关闭/获取资源操作都由Spring自动完成。

@Transactional注解中的属性

  1. noRollbackFor:指定事务在发生哪些异常的时候不回滚。
  2. rollbackFor:指定哪些异常必须回滚。
  3. readOnly:事务的只读属性。默认false。表示不是只读。
    readOnly=true。只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务。如果事务中有修改操作千万不能设置这个为true。
  4. timeout:事务的超时属性(以秒为单位)。可以指定事务方法必须在一段时间内完成,如果没有完成,自动中断方法,回滚事务。
  5. propagation:事务传播行为。
  6. isolation:事务的隔离级别。 默认就是数据库底层自己的隔离级别。

Spring事务的传播行为

传播行为定义的是事务的控制范围,Spring中共有7种传播行为。

PROPAGATION_REQUIRED

表示如果上下文中如果已经有存在事务,那么当前方法就加入的这个事务中;如果当前上下文中不存在事务,则新建事务执行。这是Spring事务默认的传播行为,它通常能满足处理大多数的业务场景。

PROPAGATION_REQUIRES_NEW

表示当前方法会启动一个新事务,并且在自己的事务中运行;如果上下文中已经存在了一个事务,则会将这个事务挂起。

PROPAGATION_SUPPORTS

表示如果上下文中如果已经有存在事务,那么当前方法就加入的这个事务中,否则就使用非事务的方式运行当前方法。

PROPAGATION_NOT_SUPPORTED

NOT_SUPPORTED是不支持的意思,表示当前方法不支持事务,如果上下文中如果已经有存在事务则会先将这个事务挂起,方法结束后再恢复事务。

PROPAGATION_NEVER

NEVER是从不的意思,这个传播行为比PROPAGATION_NOT_SUPPORTED更严格,表示当前方法不可以运行在事务中,如果上下文中如果已经有存在事务则会抛出异常。

PROPAGATION_MANDATORY

MANDATORY是强制的意思,表示执行当前方法时上下文中必须已经有存在事务,如果没有则会抛出异常。

PROPAGATION_NESTED

NESTED是嵌套的意思,表示如果上下文中如果已经有存在事务,当前方法会运行在一个嵌套事务中,否则就跟PROPAGATION_REQUIRED一样启动一个新的事务。
什么事嵌套事务呢?嵌套事务是子事务嵌套在父事务中执行,子事务是父事务的一部分。如果父事务回滚,子事务也会回滚;子事务回滚,父事务并不会回滚;事务的提交则统一由父事务提交。

Spring事务的隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。Spring中的事务定义了5中隔离级别,隔离级别越高,数据一致性就越好,但并发性越弱。

DEFAULT

这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是READ_COMMITTED。

READ_UNCOMMITTED

读未提交。表示一个事务可以读取另一个事务修改但还没有提交的数据。这种方式容易读取到脏数据。

READ_COMMITTED

读已提交。表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。

REPEATABLE_READ

可重复读。表示在事务中读取到数据之后,将这些数据加锁(悲观锁,行锁),其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。

SERIALIZABLE

串行化。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。这样可以避免任何并发问题,但会严重影响程序性能,通常情况下也不会用到该级别(锁住整个表)。

------ 本文完 ------