mybatis随笔五之Executor

Posted 颓废的悠然

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis随笔五之Executor相关的知识,希望对你有一定的参考价值。

在上一篇文章我们分析到了mapper接口方法的实现实际上是交由代理类来实现的,并最终调用Executor来查询,接下来我们对
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)这个方法进行分析。
@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
ms.getBoundSql内部调用RawSqlSource的getBoundSql方法,该方法又调用了StaticSqlSource的getBoundSql方法,并在该方法内部初始化了一个BoundSql对象,如下是BoundSql的参数
  private String sql;   //需要执行的sql语句
  private List<ParameterMapping> parameterMappings;   //参数与数据库列的对应关系
  private Object parameterObject;                     //查询传递的参数
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;
createCacheKey是调用的BaseExecutor方法根据mappedStatement的id,rowBounds的offset、limit值、要执行的sql语句、传递的参数、environment的id来创建cacheKey。
在query的时候查看是否有cache,如果有则使用cache结果,否则使用内部delegate的query方法,这里跳转到了BaseExecutor的query方法,该方法内部又使用了
queryFromDatabase方法。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> 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;
  }
方法比较简单将查询结果保存在chche中,调用doQuery方法,BaseExecutor的doQuery方法是个抽象方法,因此这里实际使用的是子类SimpleExecutor的doQuery方法
 @Override
  public <E> List<E> 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.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
我们先看看newStatementHandler这个方法,这个方法主要做了两件事情,实例化了一个RoutingStatementHandler对象,将拦截目标是statementHandler的拦截器构成拦截链。
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;
  }
RoutingStatementHandler采用的是装饰设计模式,内部delegate委托的是PreparedStatementHandler对象,因此它的构造方法内部去创建了一个PreparedStatementHandler对象
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }
构建的过程中也初始化了parameterHandler、resultSetHandler两个对象,顾名思义一个是用来处理参数的一个是用来处理结果的。
所以在创建StatementHandler的同时其余两个handler也被创建出来了。
接下来使用prepareStatement来构建参数。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }
在这个方法内部,我们最关注的是倒数二、三句。
handler.prepare类似mapper交由statementHandler的代理对象来执行,若没有针对其的拦截方法则还是调用RoutingStatementHandler的prepare方法。
public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
这里主要做了几件事:返回preparedStatement对象、设置查询超时时间、设置每次批量返回的结果行数。
handler.parameterize(stmt)方法类似也是交由statementHandler的代理对象来执行,最终也使用RoutingStatementHandler的parameterize方法。
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
在这个方法里主要是根据定义的入参的javaType、jdbcType类型来选择合适的typeHandler来设置参数,因为我们使用的是long类型,因此typeHandler使用的是LongTypeHandler。
这样我们就把preparedStatement所需的参数全部填充了,最终进入handler.<E>query(stmt, resultHandler)方法。
query方法也会先调用拦截链的方法,最后使用RoutingStatementHandler的query方法。
@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

 





  

 





















以上是关于mybatis随笔五之Executor的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis学习随笔

Mybatis-随笔

阅读随笔 SpringMybatis

MyBatis学习随笔

Mybatis框架复习随笔

Mybatis配置随笔(Idea2021)