【Mybatis源码阅读】sqlSession的执行流程

mybatis专栏 https://blog.csdn.net/worn_xiao/category_6530299.html?spm=1001.2014.3001.5482

首先看一个例子,看一下怎么使用sqlSession

  public static void setup() throws Exception {
    createBlogDataSource();
    final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
    final Reader reader = Resources.getResourceAsReader(resource);
    SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
    try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
      List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
      assertEquals(2, authors.size());
    }
  }

 这个是最常见的执行方式,好的。接下来我们可以看一下是如何执行的。

    如上图所示是sqlSession自顶向下的执行流程。依次是Sqlsession->executor->StatementHandler->Paramterhander->TypeHandler->jdbc->Typehandler->ResultSetHandler->StatementHandler->executor->sqlSession的过程。下面我们来看一下具体代码的执行流程。

可以看到SqlSession刚好包装了所有的执行方法,那么这些个方法是如何一步步的执行的呢。

首先看一下参数statement是什么,其实如果看过我上一篇https://blog.csdn.net/worn_xiao/article/details/104235116文章对Maybaties的初始化的文章的同学就知道了,其实这个statement就是Mapperstatment的id,通过他就可以拿到一个Mapperstatment,那么Mapperstatment又是什么呢,其实你可以清楚的从我上一篇博客中看到,实际上Mapperstatement就是通过注解或者是xml的方法去构建的一个select|insert|delete|update的一个方法的封装,最后再通过唯一标识符映射到 Configuration上下文环境的Map里面。那么我们接着往下走,看看它是如何工作的。

好了 功夫不负有心人,果然不出我们的预料,从Configuration里面把初始化的时候放进去的类拿出来了。好的接下来又是什么呢,你会发现executor.query()明白了不是DefautSqlSession帮我们执行的,那么是什么执行器,执行器又是什么。这里要给大家上个图

首先可以看到的是这个是一个方法的接口,没问题吧,这个接口里面封装了我们需要执行的方法,好的那么这个体系是怎么样的呢。

大家可以看一下这个是整个Executor的类的结构图,如图可以看到,在CashingExecutor里面,有引入了Executore的接口,这就类似于我们常见的设计模式中的装饰者模式,基本的执行器,执行完以后,要加上缓存在前后进行装饰,这个是我们通过基本的代码接口可以猜测出来Mybaties的源码执行流程应该是这样的。好的那么我们可以还继续我们的推理过程了。

点进去可以看到我们到了负

我们看到调用了BaseExecutor中的query方法,那么接下来呢,

@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}

如上图所示,点开看查询的方法以后,我们可以大致的看到代码如上所示,大致的意思是先查询缓存,缓存没有的话再查询数据库,接着再跟进查询数据库的方法。

打可以看到清理缓存,从数据库查询然后放入到缓存重。好的点击doQuery()

这个时候你发现跟不进去了,要让你找到具体的实现类。这里我们先随便挑一个子类,真正实现BaseExecutor的类来执行以下看。后面再来看这里是怎么实例化的,多态是怎么组织子类包装进去的,我们先跳过看SimpleExecutor类的doquery方法。

好的看一下这个就是SinmpleExcutor的doquery方法了,接下来我们看看

接下来我们看一下newStatuementHanler做了什么,可以看到首先是创建了一个StatementHandler的具体来类,然后在把statementHanlder通过interceptorChain.pluginAll()进行包装,最后返回。那么这里到底做了什么呢。首先我们要知道的就是statementHanlder到底是什么核心功能是什么?不急一会儿慢慢解释,这里顺便提一句这里的intercaptorChain的pluginALL()的执行的其实是我们的插件。什么是插件。为什么statementHanlder在执行的时候要包一层插件,这个插件可以用来做什么。本篇内容不做详细的说明,请看我后面的博客分析。这里我们还是关注于statementHanlder这个类

嗯嗯 我们可以看一下这个类的结构图。好了看了整体的架构之后,那么自然要看一看顶层接口的方法了

好的,现在我们来看看这些个方法都做了什么操作。在此之前我为什么把哪两个方法画上红框。当然证明重要了,大家看返回值应该能参出它的作用,不知道大家还记不记得最早的JDBC的代码,这里我给大家在回放一下

 DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Class.forName("com.mysql.jdbc.Driver");
  Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123");
  Statement sta = con.createStatement();

  String sql = "SELECT * FROM `user`"
  ResultSet rs = sta.executeQuery(sql);
  while(rs.next())

  {
    System.out.print(rs.getObject(1) + " ");
  }
  rs.close();
  sta.close();
  con.close();

如上代码片段所示就是jdbc的代码,最早没有Mybaties之前我们就是这样连接数据库,操作的。那么跟这个有什么关系呢。你不觉得我红框圈起来的部分的代码的返回值好像就是Statement和sql吗。好的没关系一起看一看。

好的接下来是不是已经看到了曙光。点instantiateStatement方法 

 

 每一个类有自己的实现方式,我这里调一个类来说一下,其他的你们有时间可以自己去看,那么看哪个类呢。就看一个跟上面jdbc语句一样的吧

看到了吧是不是MyBaties变成了jdbc。好了讲到这里你是不是已经知道StatementHandler实际是是准备Statement还有sql.那么再看一下getBoundSql()

实际上可以看到它就是获取到一个BoundSql

这个是什么,就是在初始化的时候对sql语句参数的封装。好了这回懂了。那么就说StatementHandler是持有BoundSql,Statement的对象。然而通过BoundSql又可获取到sql.那么请问都有了statement,sql了,让你用jdbc写一个连接操作数据库的操作你会吗。好了不扯。回到最初的地方。

这段代码我最早放出来的,给大家讲StatementHandler去了。在大家知道了StatementHandler之后接着往下看prepareStatement()这个是干嘛的

不用解释了吧,之前写的理论总要有东西证明吧。Handler.prepare就是调用持有的具体的Handler创建Statement不是么。当然你也看到了你需要的Connection是不是。这里就不跟进去了。就是connection.createstatement()都是jdbc的老套路了。不懂的就自己学习吧。

好的。回到刚刚的位置。

直接看query()方法。

我们看SimpleStatementHandler的,这个大家可以继续去看另外几个的执行情况。这里只插一句PrepareStatement比较重要。就是传说中的预编译。

是不是见证奇迹的时刻到了。一看不就是jdba吗获取sql然后execute。那么return了什么呢。这里我截个图大家自己去看了。一句话返回你要的结果集呀。

@Override

public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
  final List<Object> multipleResults = new ArrayList<Object>();
  int resultSetCount = 0;
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    while (rsw != null && resultSetCount < resultSets.length) {
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
      if (parentMapping != null) {
        String nestedResultMapId = parentMapping.getNestedResultMapId();
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
        handleResultSet(rsw, resultMap, null, parentMapping);
      }
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
  }

  return collapseSingleResultList(multipleResults);

}

细节就不说了一句话jdbc只是做了不同程度的封装。好了拿到结果集

再看一下这个代码。好了不说了。这个请听下回分解。

好的总结一下画个流程图

具体的执行过程梳理出来就是这样。每一个组件到底什么作用我也有说。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值