咱们晓得Mybatis最终是通过SqlSession对象去执行sql语句的,通过后面几篇文章咱们也晓得Mybatis是怎么解析mapper.xml文件、怎么把sql语句读入到Mybatis的Congifuration对象中、最初Mapper接口的代理对象是怎么匹配到sql语句、并最终通过SqlSession对象去执行sql语句的。
上述整个过程咱们都曾经理解过了,惟一一个尚未剖析过的问题是:SqlSession是怎么初始化的?以及他具体是怎么执行sql语句的?
明天的次要指标就是要搞清楚以上两个问题:
- SqlSession对象的创立或者初始化过程。
- SqlSession执行sql语句的具体过程。
SqlSession对象的创立
不同的我的项目,SqlSession会有不同的创立过程,但根本都大同小异。咱们明天次要剖析Springboot+Mybatis我的项目中SqlSession的创立过程。
Springboot我的项目中次要通过配置类MybatisAutoConfiguration来实现Mybatis的初始化。
MybatisAutoConfiguration次要实现:
- 创立并初始化Configuration对象
- 通过SqlSessionFactoryBean初始化SqlSessionFactory(初始化为DefaultSqlSessionFactory)
- 初始化SqlSession,Spring我的项目SqlSession初始化为SqlSessionTemplate对象
MybatisAutoConfiguration#sqlSessionFactory
sqlSessionFactory办法次要实现Configuration对象的创立,以及SqlSessionFactory对象的创立。办法比拟长,最初通过SqlSessionFactoryBean的build办法创立DefaultSqlSessionFactory对象返回,并交给Spring Ioc容器治理。
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
applyConfiguration(factory);
//省略
return factory.getObject();
MybatisAutoConfiguration#sqlSessionTemplate
sqlSessionTemplate对象是Spring+Mybatis我的项目的SqlSession落地对象,通过sqlSessionTemplate能够实现Spring和Mybatis的无缝对接。
通过下面SqlSession的类构造,咱们晓得SqlSessionTemplate理论是SqlSession的一个实现类,所以,通过@Bean注解的最终返回SqlSessionTemplate对象的办法sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)的目标就是创立SqlSession对象,并最终交给Spring的Ioc容器来治理。其中参数SqlSessionFactory是通过Spring Ioc依赖注入的DefaultSqlSessionFactory对象:
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
最终会调用到SqlSessionTemplate的SqlSessionTemplate办法:
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());
}
咱们须要把关注点放在代码最初一句话,创立了一个动静代理对象(又是动静代理)赋值给SqlSessionTelmplate对象的sqlSessionProxy,这个代理对象的回调对象为SqlSessionInterceptor,咱们对JDK动静代理曾经十分相熟了,咱们晓得所有的对于代理对象sqlSessionProxy的办法调用,最终都会调用到回调对象SqlSessionInterceptor的invoke办法。
好了,到这里咱们就晓得Mybatis的最重要的一个对象SqlSession被创立好了,他其实就是SqlSessionTelmplate对象,他的两个重要属性咱们也要搞清楚:
- sqlSessionFactory:其实就是通过参数传入进来的DefaultSqlSessionFatcory对象。
- sqlSessionProxy:其实就是以SqlSessionInterceptor为回调对象的JDK动静代理对象。
SqlSession执行sql语句的具体过程
这个问题其实上一篇文章曾经剖析过了,通过上一篇文章其实咱们曾经晓得了mapper.xml文件中的sql语句是怎么被解析到Configuration对象的mappedStatements中,并且也明确了mapper接口中的办法最终怎么匹配到Configuration对象中的mappedStatements并最终执行其中的sql语句。
整个过程咱们曾经一清二楚了,只不过,最终sql语句执行的时候是通过sqlSession对象来执行的,上一篇文章并没有详细分析sqlSession执行sql语句的具体过程。咱们当初来具体分析一下这一部分。
通过后面的剖析,当初咱们晓得sqlSession对象其实是SqlSessionTelmplate对象,咱们就以selectOne办法为例,间接看一下SqlSessionTelmplate的selectOne办法。
咱们晓得mapper.xml文件中的select语句的返回类型如果不是一个list、而是一个实体对象的时候,比方selectXXXById通过主键键值返回一条记录的时候,最终会匹配到这个selectOne办法。
看一下SqlSessionTelmplate源码,他交给sqlSessionProxy去解决,其实咱们能够看到SqlSessionTelmplate的所有的sql语句执行的办法最终都是委托给sqlSessionProxy去解决的。
咱们曾经晓得这个sqlSessionProxy其实就是动静代理对象,他最终会调用到回调对象SqlSessionInterceptor的invoke办法。
@Override
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);
}
SqlSessionInterceptor#invoke
办法源码不太长,咱们先把源码都贴出来:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
首先通过SqlSessionUtils的getSqlSessio去获取Sqlsession,Mybatis其实就是通过这个SqlSessionUtils实现与Spring的sqlSession做集成的,他首先去获取TransactionSynchronizationManager中的sqlSession,如果TransactionSynchronizationManager中存在以后线程的sqlSession的话则间接返回,不存在的话再通过DefaultSqlSessionFactory去创立sqlSession返回并存入TransactionSynchronizationManager中。
DefaultSqlSessionFactory的创立sqlSession的代码咱们后面其实曾经看过了,比方他的openSessionFromDataSource办法,最终返回的是DefaultSqlSession对象,所以真正的SqlSession其实是DefaultSqlSession对象。
获取到DefaultSqlSession对象后,执行:
Object result = method.invoke(sqlSession, args);
这句代码的意思是,通过反射机制执行sqlSession(也就是DefaultSqlSession对象)的selectOne办法。
DefaultSqlSession的selectOne办法通过获取到的数据库connection、以及事务管理等相干参数执行sql语句,这部分咱们在后面的文章中也剖析过了。
从源码中咱们能够看到SqlSession最终通过JDK动静代理的形式创立代理对象执行sql语句、而不是间接创立一个sqlSession对象去执行的目标,可能就是为了通过AOP的形式在sql语句执行实现后做一些必要的善后处理,也就是下面invoke办法中method.invoke办法执行后的commit以及closeSqlSession等相干逻辑,这样的话调用方、也就是应用层就不须要解决这些善后工作了。
以上!
上一篇 Mybatis的Mapper代理对象生成及调用过程
发表回复