咱们晓得Mybatis最终是通过SqlSession对象去执行sql语句的,通过后面几篇文章咱们也晓得Mybatis是怎么解析mapper.xml文件、怎么把sql语句读入到Mybatis的Congifuration对象中、最初Mapper接口的代理对象是怎么匹配到sql语句、并最终通过SqlSession对象去执行sql语句的。

上述整个过程咱们都曾经理解过了,惟一一个尚未剖析过的问题是:SqlSession是怎么初始化的?以及他具体是怎么执行sql语句的?

明天的次要指标就是要搞清楚以上两个问题:

  1. SqlSession对象的创立或者初始化过程。
  2. SqlSession执行sql语句的具体过程。

SqlSession对象的创立

不同的我的项目,SqlSession会有不同的创立过程,但根本都大同小异。咱们明天次要剖析Springboot+Mybatis我的项目中SqlSession的创立过程。

Springboot我的项目中次要通过配置类MybatisAutoConfiguration来实现Mybatis的初始化。

MybatisAutoConfiguration次要实现:

  1. 创立并初始化Configuration对象
  2. 通过SqlSessionFactoryBean初始化SqlSessionFactory(初始化为DefaultSqlSessionFactory)
  3. 初始化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对象,他的两个重要属性咱们也要搞清楚:

  1. sqlSessionFactory:其实就是通过参数传入进来的DefaultSqlSessionFatcory对象。
  2. 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代理对象生成及调用过程