Spring Transactional注解

前言

使用@Transactional 注解有一段时间了,今天来对它进行下总结。

再说这个之前先说下事务。

事务的基本要素(ACID)

  1. 原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

  2. 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

  3. 隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

  4. 持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

说明

Transactional注解的主要参数如下:

upload successful

下面来简单介绍下它的参数:

value:一般用来配置指定的事务管理器。

propagation:事务的传播属性,有七种。见枚举Propagation,默认REQUIRED。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//支持当前事务,如果不存在就创建一个
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
//支持当前事务,如果不存在就以非事务的方式运行
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
//支持当前事务,不存在就抛出异常
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
//创建一个新事务,如果当前有事务就暂停当前事务
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
//以非事务方式运行,如果当前有事务就暂停当前事务
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
//以非事务的方式运行,如果当前有事务就抛出异常
NEVER(TransactionDefinition.PROPAGATION_NEVER),
//如果当前有事务,就加入当前事务。
NESTED(TransactionDefinition.PROPAGATION_NESTED);

isolation:隔离级别。事务的隔离级别有4种。我们看一下Isolation枚举类。

1
2
3
4
5
6
7
8
9
10
//使用默认的隔离级别,取决于底层数据库的默认隔离级别
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
//读未提交,这种隔离级别最低,会出现脏读,不可重复读,虚读(幻读)等情况。一般不用。
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
//读已提交,这种隔离级别可以防止脏读的产生,但是无法避免不可重复读和虚读(幻读)的出现。
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
//可重复读,这种隔离级别可以防止脏读、不可重复读的出现,但是无法避免虚读(幻读)的产生。
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
//串行化,这种隔离级别最高,可以避免脏读、不可重复读和虚读(幻读)的产生。
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

Mysql默认隔离级别为可重复读。

关于事务的并发问题

  1. 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

  2. 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

  3. 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

timeout : 事务的超时时间,默认为-1,即事务直到完成都不会超时。

readOnly:是否只读,一般用来标识。拥有这个标识,写入操作不一定会失败,取决于数据库系统。

rollbackFor:事务回滚条件。参数为继承Throwable的class类。

rollbackForClassName:事务回滚条件。可以接受String数组。

noRollbackFor: 事务不会回滚条件。

noRollbackForClassName:事务不会回滚条件。

原理

Spring的Transaction注解是如何实现并生效的呢?我们来探究下。

  1. 我们首先来看看SpringTransactionAnnotationParser这个类,这个是解析transaction注解的类。
    upload successful
    调用AnnotationUtils类的getAnnotation方法拿到注解信息。
    upload successful

  2. 可以看到AnnotationTransactionAttributeSource这个类调用了parseTransactionAnnotation方法。
    upload successful
    upload successful

  3. 我们来看下AbstractFallbackTransactionAttributeSource这个类的computeTransactionAttribute方法。调用了上图的findTransactionAttribute方法。
    upload successful
    upload successful
    getTransactionAttribute方法,由于获取注解信息耗时,故spring先从缓存里获取注解事务信息,查不到在用程序获取。

  4. TransactionAspectSupport这个类的createTransactionIfNecessary方法,调用getTransactionAttribute方法。
    upload successful

  5. 我们可以看到AbstractTransactionAspect.aj文件使用了createTransactionIfNecessary方法。
    upload successful
    这个应用了aspectj。
    Before指在事务开始之前获取注解信息。
    After throwing 指当方法抛出异常后的执行动作。这里一般会进行回滚操作。
    After returning指当方法返回前的执行动作。这里一般会提交事务。
    After 指当事务完成后的动作。这里会清空当前事务注解信息。

结论

事务的隔离级别和传播属性都是我们应该掌握和学习的,对于Spring的@Transactional 注解,我们不但要会使用,而且应该深入去理解它的实现原理。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道