关于java:Mybatis的SqlSession初始化及调用过程

30次阅读

共计 5909 个字符,预计需要花费 15 分钟才能阅读完成。

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

正文完
 0