Spring作为风靡全球的Java开源框架,有着举足轻重的作用。有没有想过Spring内部是如何实现事务的?Spring除了可以设置事务的“隔离级别”,还可以额外配置事务的“传播特性”。你要知道在传播特征中,有两个特别的家伙,一个是PROPAGATION_REQUIRES_NEW,一个是PROPAGATION_NESTED。要知道,所谓的REQUIRES_NEW在级联调用该方法时,会启动一个新的事务,并暂停当前的事务;NESTED表示的嵌套事务是级联调用方法时,在嵌套事务中执行。我们都应该知道,这些传播行为是Spring特有的,与数据库无关。那么Spring底层究竟用了什么黑魔法呢?如何暂停交易,如何嵌套交易?一起来揭秘吧。毫无疑问,在数据的操作中,我们离不开交易。在银行转账操作中,我们认为如果一方扣了钱,另一方肯定会收到,而空转在很多场景中起到了关键作用。我们先以MySQL为例,来看看数据库的事务和隔离级别。然后看看这些数据库的配置和特点,以及Spring中是如何做事务的。以及Spring所谓的传播特性如何影响数据库事务,如何暂停事务,嵌套事务。什么是交易?它是第一级的原子SQL操作,一个独立的工作单元。如果工作单元中的SQL语句执行成功,则全部执行成功,如果执行失败则全部回滚。同时作为数据库事务被大众所熟知的是ACID。即数据库的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。就像应用中线程间的互斥,线程安全等,需要做很多工作,数据库也为ACID做了很多工作。隔离级别SQL标准定义了四个隔离级别,它们指定哪些在事务之间可见,哪些在事务内可见。READUNCOMMITTED(读未提交)-->两个事务之间,一个事务没有提交,但是已经被另一个看到了。READCOMMITTED(commitreading)-->两个事务之间,只有一个事务commit后,另一个事务才能看到。REPEATABLEREAD(可重复读)-->在一个事务的执行过程中,多次读的结果是一样的,即使其他事务做出改变,也会“隐形”SERIALIZABLE(可序列化)-->最高隔离级别,强制串行执行的事务。由于这些隔离级别,会造成所谓的“脏读”、“幻读”、“不可重复读”等问题,因此需要根据具体场景选择适用的隔离级别。如何设置隔离级别对于MySQL的隔离级别,可以在全局设置,也可以在Session级别设置。官方文档设置语法如下:当我们通过客户端连接到MySQLServer时,我们可以做一些配置,也可以通过jdbcURL看到。此设置在会话级别。参考上面官方文档,设置session隔离级别的命令是这样的:setsessiontransactionisolationlevelSERIALIZABLE;请注意,MySQL的默认隔离级别是REPEATABLEREAD,这是我更改的。同时在这里查询当前隔离级别的SQL,旧版本为SELECT@@TX_ISOLATION;,新版本为SELECT@@Transaction_ISOLATION;注意区分。Spring事务是怎么回事看完了数据库的隔离级别,我们再来看看Spring中的实现。Spring中隔离级别的定义有5种,其中4种与数据库一致,其中包含一种DEFAULT,表示直接使用数据库定义,无需具体设置。我们看到数据库的隔离级别可以设置在GLOBAL级别,也可以设置在SESSION级别,每个SESSION代表一个连接。在Spring中,我们通过独立的“连接”操作数据库,完成CRUD。至此,我们应该可以知道,Spring级别的设置是通过会话级别的隔离来设置的,从而对应到数据库中的事务隔离级别。传播特性呢?由于数据库不直接支持这样的特性,Spring根据不同的传播特性需求,采用迂回的方式实现。总结一下,对于级联操作,如果是REQUIRED_NEW,所谓挂起当前事务,开启新事务,就是说Spring已经申请了一个新的Connection,会对应一个新的事务,然后把这个connection绑定到当前线程,然后继续执行;对于嵌套事务,底层是通过数据库的SAVEPOINT实现所谓的“子事务”。如果熟悉代码DEBUG过程中每个方法对应的Frame,可以类推,如果执行失败回滚,可以指定回滚到当前事务的某个SAVEPOINT,而你不需要回滚所有这些。在Spring层面,通过JDBC3.0实现,见下方代码注释。*
此事务管理器通过JDBC3.0*{@linkjava.sql.Savepoint}机制支持嵌套事务。*{@link#setNestedTransactionAllowed"nestedTransactionAllowed"}标志默认*为"true",既定事务将在JDBC*驱动程序上不受限制地工作,支持此类保存点的JD代码(例如:/***Suspendthegiventransaction。首先暂停事务同步,*然后委托给{@codedoSuspend}模板方法。*@paramtransaction当前事务对象*(或{@codenull}只暂停活动同步,如果有)*@returnanobjectthatholdssuspendedresources*(或{@codenull}ifneithertransactionnorsynchronizationactive)*@see#doSuspend*@see#resume*/@NullableprotectedResourcesHusoldersHusoldersSus暂停(@NullableObjecttransaction)throwsTransactionException{if(TransactionSynchronizationManager.isSynchronizationActive()){List