前言
使用@Transactional 注解有一段时间了,今天来对它进行下总结。
再说这个之前先说下事务。
事务的基本要素(ACID)
原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
说明
Transactional注解的主要参数如下:
下面来简单介绍下它的参数:
value:一般用来配置指定的事务管理器。
propagation:事务的传播属性,有七种。见枚举Propagation,默认REQUIRED。
1 | //支持当前事务,如果不存在就创建一个 |
isolation:隔离级别。事务的隔离级别有4种。我们看一下Isolation枚举类。
1 | //使用默认的隔离级别,取决于底层数据库的默认隔离级别 |
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
Mysql默认隔离级别为可重复读。
关于事务的并发问题
脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
timeout : 事务的超时时间,默认为-1,即事务直到完成都不会超时。
readOnly:是否只读,一般用来标识。拥有这个标识,写入操作不一定会失败,取决于数据库系统。
rollbackFor:事务回滚条件。参数为继承Throwable的class类。
rollbackForClassName:事务回滚条件。可以接受String数组。
noRollbackFor: 事务不会回滚条件。
noRollbackForClassName:事务不会回滚条件。
原理
Spring的Transaction注解是如何实现并生效的呢?我们来探究下。
我们首先来看看SpringTransactionAnnotationParser这个类,这个是解析transaction注解的类。
调用AnnotationUtils类的getAnnotation方法拿到注解信息。可以看到AnnotationTransactionAttributeSource这个类调用了parseTransactionAnnotation方法。
我们来看下AbstractFallbackTransactionAttributeSource这个类的computeTransactionAttribute方法。调用了上图的findTransactionAttribute方法。
getTransactionAttribute方法,由于获取注解信息耗时,故spring先从缓存里获取注解事务信息,查不到在用程序获取。TransactionAspectSupport这个类的createTransactionIfNecessary方法,调用getTransactionAttribute方法。
我们可以看到AbstractTransactionAspect.aj文件使用了createTransactionIfNecessary方法。
这个应用了aspectj。
Before指在事务开始之前获取注解信息。
After throwing 指当方法抛出异常后的执行动作。这里一般会进行回滚操作。
After returning指当方法返回前的执行动作。这里一般会提交事务。
After 指当事务完成后的动作。这里会清空当前事务注解信息。
结论
事务的隔离级别和传播属性都是我们应该掌握和学习的,对于Spring的@Transactional 注解,我们不但要会使用,而且应该深入去理解它的实现原理。