之前被同事问了一个问题:在我们的工程里,事务的开启跟关闭是由Spring负责的,但具体的SQL语句却是由Mybatis执行的。那么问题来了,Mybatis怎么保证自己执行的SQL语句是处在Spring的事务上下文中?
原文地址:www.jianshu.com/p/6a880d20a…
注:这篇文章重点不是分析Spring事务的实现原理,但却需要读者提前了解Spring事务原理的一些知识点,这样读起来才会容易些
现在公司主流的开发框架大部分是使用spring+mybatis来操作数据库,所有的事务操作都是交给spring去管理。当我们需要一个有事务上下文的数据库操作时,我们的做法就是写一个操作数据库的方法,并在该方法上面加上@Transactional注解就可以了。
仔细思考一下这个过程,@Transactional是由spring进行处理的,spring做的事情是从数据源(一般为数据库连接池,比如说druid,c3p0等)获取一个数据库连接,然后在进入方法逻辑前执行setAutoCommit(false)操作,最后在处理成功或者出现异常的时候分别执行commit或者rollback操作。
那么问题来了,开启跟结束事务是由spring获取到数据库连接以后进行操作的,但我们实际执行的update或者insert语句却是由mybatis获取数据库连接进行操作的,可以想到如果想让事务生效,那么spring跟mybatis使用的必须是同一个连接,真实情况是什么样呢?它们之间如何进行无缝衔接?让我们通过源码来分析一下。
首先如果想在spring中使用mybatis,我们除了引入mybatis依赖以外,还需要引入一个包:mybatis-spring。
1 | 复制代码<dependency> |
可以猜测这个依赖包应该就是Spring跟Mybatis进行无缝连接的关键。
一般来说,我们在工程中的配置文件往往是这样:
1 | 复制代码<!--会话工厂 --> |
注:
1.会话工厂sqlSessionFactory跟Spring事务管理器transactionManager所使用的数据源dataSource必须是同一个。
2.这里的sqlSessionFactory类型是org.mybatis.spring.SqlSessionFactoryBean,该类是由我们引入的包mybatis-spring提供的。
看名字就知道SqlSessionFactoryBean是一个工厂bean,也就是说它交给Spring的真正实例是由getObject()方法提供的,那么我们去看下它真正实例初始化源码:
1 | 复制代码@Override |
下面我们再去看看SpringManagedTransactionFactory类的源码:
1 | 复制代码public class SpringManagedTransactionFactory implements TransactionFactory { |
代码很少,且只有一个方法是有效的,看来离成功越来越近了,继续跟进去看看SpringManagedTransaction的源码:
1 | 复制代码@Override |
省略该类中其他部分,我们重点看获取连接的地方,这里最关键的地方就在this.connection = DataSourceUtils.getConnection(this.dataSource);,
DataSourceUtils全名是org.springframework.jdbc.datasource.DataSourceUtils,没错,它是由Spring提供的类,根据我们之前的猜测,Spring开启事务以后,Mybatis要想让自己的SQL语句处在这个事务上下文中操作,那必须拿到跟Spring开启事务同一个数据库连接才行,由于DataSourceUtils类是由Spring提供的,看来跟我们开始猜测的结果类似,我们接下来看看DataSourceUtils源码验证一下:
1 | 复制代码//获取数据库连接最终落在该方法上,我删除一些不重要的代码 |
看到TransactionSynchronizationManager有没有很亲切的感觉?对Spring事务管理源码熟悉的同学会马上联想到Spring开启事务以后,就是把相应的数据库连接放在这里,我截取源码看一下:
1 | 复制代码if (txObject.isNewConnectionHolder()) { |
这段代码具体就是在我们上面配置的org.springframework.jdbc.datasource.DataSourceTransactionManager类中的doBegin方法里。至于TransactionSynchronizationManager类的实现原理其实我觉得你已经猜到了,没错,就是Java中经典类库ThreadLocal类!!!
最后补上一张图来说明spring+mybatis事务过程数据源获取逻辑:
Spring-Mybatis事务处理过程
本文转载自: 掘金