登录
首页 >  数据库 >  MySQL

Mybatis源码分析(六):执行sql

来源:SegmentFault

时间:2023-02-16 15:39:30 285浏览 收藏

知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个数据库开发实战,手把手教大家学习《Mybatis源码分析(六):执行sql》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!

获取SqlSession后,下一步就是执行sql.

User user=sqlSession.selectOne("last.soul.mapper.UserMapper.selectById",map);

DefaultSqlSession的主要功能就是实现增删改查功能,以及它们的重载方法。就查询来说,最后都会调用select方法,然后改装成selectOne,selectMap等,代码如下:

/**
   * 
   * @param statement sql语句ID=xxxMapper.xml文件中的namespace+sql标签的id.
   *                  如:last.soul.mapper.UserMapper.selectById
   * @param parameter sql的参数
   * @param rowBounds 分页信息
   * @param 
   * @return
   */
  @Override
  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //sql相关消息的包装对象,比如:sql语句,返回类型,是否使用缓存等
      MappedStatement ms = configuration.getMappedStatement(statement);
      //执行sql
      return executor.query(ms, wrapCollection(parameter)/*包装集合类型参数*/, rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

由上文得知executor是CachingExecutor,执行的是CachingExecutor的query方法。

public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List list = (List) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //delegate是SimpleExecutor对象
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

query方法中主要逻辑是判断是否有缓存,有缓存返回缓存值,否则执行delegate对象的query方法,即SimpleExecutor对象的query方法,然而你会发现该类中没有些方法,那它执行的就是他的父类BaseExecutor的query方法。query方法又会调用它的私有方法queryFromDatabase。

image.png

queryFromDatabase源代码如下:

private  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      //调用子类的实现
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

其中最关键的代码就是调用了子类(SimpleExecutor类,CachingExecutor类没有实现该方法)的doQuery方法。源代码如下:

@Override
  public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

这个方法的主要逻辑是创建StatementHandler,然后调用它的query方法。打开configuration.newStatementHandler,我们发现生成的是RoutingStatementHandler对象。源代码如下:

 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

所以SimpleExecutor类doQuery()方法最后一行执行的query方法是执行的我们发现生成的是RoutingStatementHandler看的query方法。它的源代码如下:

public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }

delegate代理的是哪个StatementHandler对象呢?这个对象在RoutingStatementHandler构造方法中赋值的。

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

ms.getStatementType()在本例中使用的是默认值。MappedStatement的内部类Builder源代码中。

      mappedStatement.statementType = StatementType.PREPARED;

所以delegate为PreparedStatementHandler对象,最终调用的也是PreparedStatementHandler下的query方法,源代码如下:

public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

逻辑比较清晰,1、执行sql。2、处理返回结果。

今天关于《Mybatis源码分析(六):执行sql》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表