咱们晓得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代理对象生成及调用过程