SpringBoot集成MyBatis原理

一.MyBatis是怎樣與SpringBoot集成的?

SpringBoot開啟自動(dòng)配置,在ConfigurationClassPostProcessor這個(gè)BeanFactory的后置處理器中,會(huì)讀取spring.factories配置文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration,讀取到所有的自動(dòng)配置類進(jìn)行注冊(cè)。我們引入mybatis-spring-boot-starter之后,在上述BeanFactory的后置處理器中就會(huì)讀取mybatis-spring-boot-autoconfigure-***.jar包中的spring.factories文件:

spring.factories

我們看到有MybatisAutoConfiguration,在這個(gè)類中聲明了SqlSessionFactory和SqlSessionTemplate,這樣我們實(shí)際開發(fā)中就可以直接注入SqlSessionTemplate進(jìn)行開發(fā)了。

二.我們開發(fā)過程中創(chuàng)建的是Mapper接口,那我們注入到Service中的是什么對(duì)象呢?

再使用java配置的時(shí)候,由MapperScan這個(gè)注解來注冊(cè)Mapper。MapperScan注解引入了MapperScannerRegistrar,這是一個(gè)ImportBeanDefinitionRegistrar,我們來看它的registerBeanDefinitions方法,其調(diào)用棧如下:

1.org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)

2.org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.core.annotation.AnnotationAttributes, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.String),該方法中聲明要注冊(cè)的對(duì)象是:MapperScannerConfigurer,繼承關(guān)系如下:

MapperScannerConfigurer

可以看出他是一個(gè)BeanFactory的后置處理器,我們看它的postProcessBeanDefinitionRegistry方法調(diào)用棧:

1.org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry

2.org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan

3.org.mybatis.spring.mapper.ClassPathMapperScanner#doScan

4.org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions在這個(gè)方法中,對(duì)于每一個(gè)Mapper,都注冊(cè)了一個(gè)MapperFactoryBean,這是一個(gè)FactoryBean,Bean實(shí)例化的時(shí)候會(huì)調(diào)用它的getObject方法

5.org.mybatis.spring.mapper.MapperFactoryBean#getObject

6.org.mybatis.spring.SqlSessionTemplate#getMapper

7.org.apache.ibatis.session.Configuration#getMapper(從這里開始,回到了MyBatis框架本身)

8.org.apache.ibatis.binding.MapperRegistry#getMapper

9.org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)

10.org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.binding.MapperProxy<T>),在這個(gè)方法中,使用JDK動(dòng)態(tài)代理,創(chuàng)建代理對(duì)象,該代理對(duì)象就是我們Service層的代碼中注入的對(duì)象。

三.MyBatis的SqlSession是非線程安全的,那Spring的SqlSessionTemplate是怎么解決的這個(gè)問題?

SqlSessionTemplate是在自動(dòng)配置的時(shí)候創(chuàng)建的,我們看一下它的創(chuàng)建流程:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,

? ? PersistenceExceptionTranslator exceptionTranslator) {

notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");

? notNull(executorType, "Property 'executorType' is required");

? this.sqlSessionFactory = sqlSessionFactory;

? this.executorType = executorType;

? this.exceptionTranslator = exceptionTranslator;

? this.sqlSessionProxy = (SqlSession)newProxyInstance(SqlSessionFactory.class.getClassLoader(),

? ? ? new Class[] { SqlSession.class }, new SqlSessionInterceptor());

}

看到通過JDK動(dòng)態(tài)代理創(chuàng)建了一個(gè)sqlSessionProxy,后續(xù)數(shù)據(jù)庫的增刪改查都是通過這個(gè)sqlSessionProxy。我們著重看一下SqlSessionInterceptor的invoke方法:

1.org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke

2.org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator):

getSqlSession

事務(wù)同步管理器可以認(rèn)為是一個(gè)ThreadLocal的map,這樣同一個(gè)線程在使用SqlSessionTemplate多次對(duì)數(shù)據(jù)庫操作的時(shí)候,取到的是同一個(gè)SqlSession;而不同的線程取到的是不同的SqlSession,所以說SqlSessionTemplate是線程安全的。

四.MyBatis怎么跟Spring聲明式事務(wù)配合完成的數(shù)據(jù)庫事務(wù)內(nèi)的增刪改查操作?

我們知道事務(wù)要在同一個(gè)數(shù)據(jù)庫連接上實(shí)現(xiàn),事務(wù)操作分以下三步:

1.設(shè)置數(shù)據(jù)庫連接的自動(dòng)提交為false

2.通過這個(gè)數(shù)據(jù)庫連接進(jìn)行sql語句執(zhí)行

3.異?;貪L或者提交

以上三步中的1和3是Spring聲明式事務(wù)做的,2是MyBatis做的,它們是怎么配合的呢?其實(shí)只需要保證1、2、3這三步中獲取到的是同一個(gè)數(shù)據(jù)庫連接就好,其實(shí)也是通過事務(wù)同部管理器這個(gè)ThreadLocal的map來實(shí)現(xiàn)的。

下面我們分別跟蹤以下這三步中是如何獲取數(shù)據(jù)庫連接的:

首先看開始事務(wù)也就是上述第1步:

1.org.springframework.transaction.interceptor.TransactionInterceptor#invoke

2.org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

3.org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary

4.org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

5.org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction,該方法中有:ConnectionHolder conHolder =

(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());也就是從事務(wù)同部管理器中獲取連接。

其次我們看第2步執(zhí)行sql的時(shí)候:

我們直接看SqlSessionTemplate對(duì)象在執(zhí)行數(shù)據(jù)庫操作時(shí)所使用的sqlSessionProxy這個(gè)代理對(duì)象的方法執(zhí)行過程:

1.org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke

2.org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator)

3.org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(org.apache.ibatis.session.ExecutorType)

4.org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

openSessionFromDataSource?

5.SqlSesssion通過Executor執(zhí)行sql獲取數(shù)據(jù)庫連接的代碼如下:

getConnection

6.org.mybatis.spring.transaction.SpringManagedTransaction#getConnection

7.org.mybatis.spring.transaction.SpringManagedTransaction#openConnection

8.org.springframework.jdbc.datasource.DataSourceUtils#getConnection

9.org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

doGetConnection

可以看到也算是從事務(wù)同部管理器中獲取連接,也就是同一個(gè)線程中取到的是同一個(gè)連接。

最后看事務(wù)異?;貪L和正常提交:

回滾和提交也都是用第1步創(chuàng)建的數(shù)據(jù)庫連接,從org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction的執(zhí)行流程可以看出:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容