前言
在基于Spring框架的程序中我们一般会使用Spring提供的事务管理器对事务进行管理。Spring支持编程式事务以及声明式事务,声明式事务使用@Transactional注解标志方法,表示开启事务,使用注解较为简洁、方便,也是Spring中较常用的开启事务的方式,为了能灵活使用@Transactional注解,有必要对该注解的实现深入了解。
通篇文章由介绍事务开启,再逐步过渡到Spring对事务的支持,以及如何使用Spring管理事务,之后再深入介绍@Transactional注解的核心源码,最后以讲解@Transactional注解使用的注意事项完结整篇文章。
事务的概念
事务是由不可分割的一系列操作组成的工作单元,这些操作紧密相连、密不可分,要么同时执行成功,要么同时执行失败。
事务遵循ACID原则:
- A(Atomicity):原子性,在开启事务的前提下,操作执行的过程中,任何一个操作失败都会导致事务执行失败。
- C(Consistency):一致性,事务的执行结果总是使得数据由一个一致性的状态转变为另一个一致性的状态。
- I(Isolation):隔离性,在多个事务开启的前提下,多个事务同时执行不会相互造成影响。
- D(Durability):持久性,事务执行成功后,对数据状态造成的改变是永久的,不会因为机器发生故障等其他问题而造成数据丢失。
事务的作用
上一段简单的介绍了事务的概念,那程序中为什么需要使用事务呢?接下来将从事务遵循的ACID原则展开讲述事务在程序中的作用。
举一个常用的银行转账例子:
账户A、账户B分别有2000元、5000元存款,账户A向账户B转账1000元,这个动作从开始到最终完成大致需要经过以下几个步骤:
- 检查账户A余额是否大于2000
- 从账户A扣除1000元
- 将1000元转入账户B
原子性
试想一下转账流程未开启事务,在执行的过程中,账户A成功扣除1000元后,账户B还未被转入1000元,此时系统发生了故障,就可能会引发一个问题,账户A已经成功扣除了1000元,但账户B未入账,最终导致1000元不翼而飞。
每个系统都不是十全十美的,我们无法预料系统什么何时回发生故障,但我们能做的就是在事故发生时,我们的系统拥有能应对事故的能力,这次消失的1000元是由于未保障原子性导致的。没有保障的系统会引发用户的信任危机,最终使得系统被抛弃,甚至使得业务走向失败。转账这个功能是一个完整的业务,这个业务下的所有操作应该保持一致的执行结果要么一同成功,要么一同失败。
一致性
账户A向账户B转账3000元,但账户A只有2000元,在大众的认知范围内银行账户余额是不允许为负数的,所以这次转账最终应是失败的,如果出现了账户余额负数的情况,这就破坏了事务的一致性,符合事务的工作单元需要保证一致性。
隔离性
现代的程序大都是支持并发运行的,就会出现多个方法同时执行的情况,如线程1操作账户A向账户B转账1000元,同一时间线程2也操作账户C向账户B转账1000元,线程1与线程2进行运算时拿到的账户B的余额都是5000元,两个线程运算最终结果都是账户B的余额是6000元,落到库中账户B的最终余额就是6000元,但实际上账户A和C都向B转1000元,账户B应该是7000元的,消失的1000元去哪了呢?这就是未保障隔离性可能发生的后果,多线程并发操作数据库未开启事务,没有隔离性作保障,最终的结果会引发很多问题。
持久性
账户A向账户B成功转账1000元后,账户A的余额为1000元,账户B的余额为6000元,这个状态是持久的,无论是否数据库宕机,还是发生其他的故障,这个状态都不会变。
Spring对事务的支持
Spring并不直接管理事务,而是提供了三个核心接口:PlatformTransactionManager、TransactionDefinition、TransactionDefinition,供各个ORM平台实现个性化的管理事务的方式。
同时Spring为我们提供了两种方式使用事务:编程式事务管理功能、声明式事务管理功能。编程式事务与声明式事务相比较,编程式事务对代码有侵入性,而声明式事务使用起来简便,只需要编写少量与事务管理相关的代码,并与业务代码相分离无耦合,但声明式事务能实现最细粒度的事务管理级别是方法级的,需要更细粒度只能使用编程式事务。
核心接口:
PlatformTransactionManager
PlatformTransactionManager接口为我们提供了三个方法:
public interface PlatformTransactionManager extends TransactionManager { // 根据事务的定义创建事务,并返回事务当前的状态 TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; // 根据事务状态提交事务 void commit(TransactionStatus status) throws TransactionException; // 根据事务状态回滚事务 void rollback(TransactionStatus status) throws TransactionException; }
Spring并不直接管理事务,而是提供了统一的PlatformTransactionManager接口,下图是展示了PlatformTransactionManager部分子类,各个ORM平台通过实现该接口提供具体的事务管理机制,如jdbc与iBatis提供的该接口实现类为DataSourceTransactionManager。
TransactionDefinition
TransactionDefinition是事务的定义,包含了事务常用的属性,包括事务的传播行为、隔离级别、事务超时时间。Spring根据TransactionDefinition创建事务。
public interface TransactionDefinition { // 事务传播行为 default int getPropagationBehavior() { return PROPAGATION_REQUIRED; } // 事务隔离级别 default int getIsolationLevel() { return ISOLATION_DEFAULT; } // 事务超时时间 default int getTimeout() { return TIMEOUT_DEFAULT; } // 事务只读状态 default boolean isReadOnly() { return false; } // 事务名称 @Nullable default String getName() { return null; } }
TransactionStatus
TransactionStatus记录了事务运行的状态,包括当前事务是否新事物,是否有保存点,是否被标记为rollback-only。
public interface TransactionStatus extends SavepointManager, Flushable { // 是否新事物 boolean isNewTransaction(); // 是否内部存在保存点 boolean hasSavepoint(); // 将事物标记为回滚 void setRollbackOnly(); // 是否事物被标记为rollback-only boolean isRollbackOnly(); // 将底层的会话刷新至数据库 @Override void flush(); // 事物是否完成,指事物是否已经提交或回滚 boolean isCompleted(); }
TransactionStatus实现了SavepointManager接口,SavepointManager包含了操作savepoint的三个方法
public interface SavepointManager { // 创建保存点,后续可以调用rollbackToSavepoint()回滚到标记的保存点 Object createSavepoint() throws TransactionException; // 回滚保存点 void rollbackToSavepoint(Object savepoint) throws TransactionException; // 释放指定的savepoint保存点, void releaseSavepoint(Object savepoint) throws TransactionException; }
事务的创建、回滚、提交都是基于该TransactionStatus接口完成。
两种事务管理方式
使用编程式事务管理
- 通过PlatformTransactionManager控制事务,需要手动执行回滚或提交事务
@Configuration public class AppConfig { @Bean public DataSource dataSource(){ DataSource dataSource = new DataSource(); dataSource.setDriverClassName("driverClassName"); dataSource.setUrl("url"); dataSource.setUsername("username"); dataSource.setPassword("password"); return dataSource; } @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
@Service public class TestAServiceImpl implements TestAService { @Autowired private PlatformTransactionManager txManager; @Override public void