之前分享的几篇文章可以一起看,形成一个体系
【Mybatis】一级缓存与二级缓存源码分析与自定义二级缓存
【Spring】Spring事务相关源码分析
【Mybatis】Mybatis数据源与事务源码分析
Spring与Mybaitis融合
SpringManagedTransaction:
org.mybatis.spring.transaction.SpringManagedTransaction :mybatis处理事务的类,打通了 MyBatis 的事物管理、连接管理 和 spring-tx 的 事物管理、连接管理,使得 MyBatis 与 Spring 可以使用统一的方式来管理连接的生命周期 和 事务处理。
org.mybatis.spring.transaction.SpringManagedTransactionFactory:创建SpringManagedTransaction
SqlSession:
org.apache.ibatis.session.SqlSession
->
org.mybatis.spring.SqlSessionTemplate:mybatis与spring整合后执行sql时使用的就是这个实现,它是通过内部持有的SqlSession代理对象来执行sql
->
org.apache.ibatis.session.SqlSessionFactory->DefaultSqlSessionFactory:创建SqlSession(DefaultSqlSession)
TransactionSynchronizationManager:
TransactionSynchronizationManager:事务同步器,会将连接资源绑定到 ThreadLocal 变量中,如果是在同一个事务当中的话,就可以通过 TransactionSynchronizationManager 中的 ThreadLocal 变量来获取到同一个连接资源。
- mybatis-spring.jar 是通过 SqlSessionTemplate 来创建 SqlSession 的代理 sqlSessionProxy;
- sqlSessionProxy 会通过 SqlSessionInterceptor 来对 SqlSession 中的每个 sql 操作进行拦截,从而使用 spring-tx 的事务同步器 TransactionSynchronizationManager 中管理的 SqlSession 来执行 sql。
- 在执行 sql 前,是通过 SpringManagedTransaction 来获取连接和管理事物的。
- 如果是 @Transactional 标记的事物方法,SpringManagedTransaction 就会放弃事物的管理,交由 spring-tx 的 TransactionInterceptor 来进行 aop 拦截,从而管理事物。
MyBatis 原生的 连接管理 和 事物管理:
MyBatis 原生的 连接管理 和 事物管理 是交给 org.apache.ibatis.transaction.Transaction 来管理的。
Spring-tx 主要封装的是事物管理,事物管理操作是通过 DataSourceTransactionManager 来实现的。而连接的管理是通过 org.springframework.jdbc.datasource.DataSourceUtils 来操作具体的 DataSource 来实现的。
MyBatis 与 Spring-tx 的事物管理的整合
MyBatis 与 Spring-tx 的事物管理的整合是通过 mybatis-spring-.jar 中的 SpringManagedTransaction 来完成的。
SpringManagedTransaction 打通了 MyBatis 的事物管理、连接管理 和 spring-tx 的 事物管理、连接管理,使得 MyBatis 与 Spring 可以使用统一的方式来管理连接的生命周期 和 事务处理。
MyBatis 与 Spring 结合之后,sql 的执行具体会通过实现类 org.mybatis.spring.SqlSessionTemplate 来完成。
SqlSessionTemplate 每次在执行 sql 时,都会被 SqlSessionInterceptor 进行拦截,拦截后会通过 Spring 的事务同步器 TransactionSynchronizationManager 获取到当前的 SqlSession 去执行 sql 操作。
SqlSessionInterceptor 保证了 MyBatis 的 SqlSession 在执行 sql 时使用的连接与 Spring 事物管理操作使用的连接是同一个连接。
TransactionSynchronizationManager
作用:
TransactionSynchronizationManager是事务同步管理器。 管理每个线程的资源和事务同步。
在原生JDBC中我们获取连接的connection 是非线程安全的,因为每一个数据库操作都要获取一个connection对象。不能只创建一次,共享connection。否则会出现数据不一致的情况。
所以connection本身是线程不安全的,并且connection创建开销比较大,所以一般使用数据库连接池来统一的管理connection对象,例如druid连接池、c3p0连接池、连接池等。
而spring帮我管理事务的情况下,在使用事务的情况下 ,实际上是在ConnectionHolder中获取的Connection。而ConnectionHolder是在TransactionSynchronizationManager中获取的resources属性的值,即connection对象信息。
重点源码
现在我们看着这里衔接这整个事务去操作数据源的数据。
下面就是我们熟悉的,通过下面,我们可以使用当前的类,去拿到我们去操作数据的链接或者SqlSession
- 数据源-》ConnectionHolder
- SqlSessionFactory-》SqlSessionHolder
那这两个是什么时候放进去的呢?
1.第一个是在执行insert之前,放进去的
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
2.第二个是在执行insert的时候放进去,以便后面使用的
SqlSessionUtils
org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator)
第一次获取的时候还为null,所以拿不到SqlSessionHolder
放置是在下面进行的
org.mybatis.spring.SqlSessionUtils#registerSessionHolder
放进去了
这时候 事务同步管理器就保存上了当前线程的数据源和执行所需要的sqlsession类等信息了。
后面很多地方都可以通过下面的代码获取到我们的ConnectionHolder和 SqlSessionHolder。
- ConnectionHolder、SqlSessionHolder 的工作机制是:我们将Connection对象放在一个全局公用的地方,然后在不同的操作中都从这个地方取得Connection,从而完成Connection共享的目的,但是要记住 在当前线程完成之后会把 此对象销毁掉
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
总结:
commit执行流程
准备事务
com.qax.ztb.machine.facade.TestController#commitTest
->
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept:Cglib中方法用于方法拦截的Interceptor拦截器
->
org.springframework.aop.framework.ReflectiveMethodInvocation#ReflectiveMethodInvocation:从Cglib代理涉及的Callback之DynamicAdvisedInterceptor织入增强流程,初始化ReflectiveMethodInvocation。并且植入 TransactionInterceptor
->
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed :回调此方法,通过TransactionInterceptor去反射调用invoke
->
org.springframework.transaction.interceptor.TransactionInterceptor#invoke :执行事务植入
->
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction:实际植入事务方法
->
org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary:创建TransactionInfo事务信息类
-->
org.springframework.transaction.PlatformTransactionManager#getTransaction:创建 TransactionStatus
-->
org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction:实际创建 TransactionStatus类方法
-->
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin:给 DataSourceTransactionObject 进行事务参数配置,并且绑定DataSource和ConnectionHolder到ThreadLocal
-->
org.springframework.transaction.support.AbstractPlatformTransactionManager#prepareSynchronization:初始化 TransactionSynchronizationManager其他事务参数,均绑定到 ThreadLocal 中
-->
org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo:将TransactionInfo注入新的属性准备完成此类
-> org.springframework.transaction.interceptor.TransactionAspectSupport.CoroutinesInvocationCallback#proceedWithInvocation:回调
->
actionLogMapper.insert:开始执行我们的新增操作
执行操作,提交事务
actionLogMapper.insert(actionLogEntity);
->
com.baomidou.mybatisplus.core.override.MybatisMapperProxy#invoke:在项目启动中,已经把每一个Mapper都设置了MybatisMapperProxy的代理,所以具体执行的话,就要走invoke方法去执行具体操作。注意:这个时候sqlSession也已经在初始化中准备好了。
-->
com.baomidou.mybatisplus.core.override.MybatisMapperProxy.PlainMethodInvoker#invoke:然后通过 MybatisMapperMethod的invoke方法去调用具体的执行逻辑。注意sqlSession等于SqlSessionTemplate。在构建SqlSessionTemplate的时候,已经通过反射把sqlSessionProxy创建完成并且植入了SqlSessionInterceptor,所以调用SqlSessionTemplate的方法,会被拦截。
->
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke:拦截调用,获取SqlSession,进行commit
-->
org.springframework.transaction.support.TransactionSynchronizationManager#getResource:这个时候会获取数据库连接
-->
org.springframework.transaction.support.TransactionSynchronizationManager#getResource:通过key(DataSoure)获取到我们前面创建的 ConnectionHolder,在里面就可以获得我们设置的DataSoure了
-->
之后就直接调用操作数据库即可。
->
org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning:回调此方法进行实际的事务提交操作
-->
org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit
-->
org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit:最终在这里提交:通过DataSourceTransactionObject,得到ConnectionHolder中的Connection,进行commit
->
最后清理,结束。