Mybatis源码分析-BaseExecutor

根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下BaseExecutor如何解析执行sql语句

BaseExecutor-抽象类

其是Executor接口的实现类但为抽象类,另外一个则为具体实现类为CachingExecutor,主要是通过对象适配的设计模式在原来的executor上再附上缓存的属性,有兴趣的可自行查阅。先从构造函数看一发

  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    //事务对象
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    //表明exector的状态
    this.closed = false;
    //主文件属性,主要获取MappedStatement对象
    this.configuration = configuration;
    this.wrapper = this;
  }

BaseExecutor#update()-SqlSession之insert/update/delete入口

具体源码如下

  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache();
    //供子类复写执行CUD操作
    return doUpdate(ms, parameter);
  }

BaseExecutor#query()-SqlSession之select入口

具体源码如下

  @Override
  public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取绑定的sql,并将参数对象与sql语句的#{}一一对应
    BoundSql boundSql = ms.getBoundSql(parameter);
    //获取cacheKey供缓存,包含完整的语句、参数等
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //比原先多传入CacheKey和BoundSql参数
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

具体的查询处理逻辑如下

  @Override
  public  List 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 list;
    try {
      queryStack++;
      //如果查询的语句已存在本地缓存中,则直接从本地获取,反之从数据库中读取内容
      list = resultHandler == null ? (List) localCache.getObject(key) : null;
      if (list != null) {
        //此处尝试对Callable类型的表达式进行处理,主要是针对mode=out类型的参数
        //此参数主要是通过map来定义,直接从map中获取
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //从数据库中获取并进行缓存处理,其也会调用子类需复写的doQuery()方法
        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;
  }

由上可知,BaseExecutor对CRUD操作均转化为对子类的doUpdate()/doQuery()方法的调用,并一般都会相应的结果进行缓存以免频繁请求数据库导致性能下降。本文则从SimpleExecutor子类来进行分析

SimpleExecutor

分别看下SimpleExecutor复写的doUpdate()和doQuery()方法,具体源码如下
doUpdate()

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler来处理update()
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //创建表达式对象Statement
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
  }

doQuery()

  @Override
  public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler来处理query()
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    //创建表达式对象Statement
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  }

doUpdate()/doQuery()代码的执行逻辑一致,均是先创建StatementHandler对象,然后通过prepareStatement()方法创建表达式对象,供前者调用处理update/query方法

SimpleExecutor#prepareStatement()-创建预表达式对象

逻辑如下

  //handler对象对应的为RoutingStatementHandler对象,其实也是个适配管理类
  //可根据MappedStatement的statementType来确定表达式处理handler类,后续讲解
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //获取连接对象,如果该日志等级为debug,则会打印相应的处理日志,采用代理实现
    Connection connection = getConnection(statementLog);
    //创建真实的Statement对象,比如SimpleStatement/PreparedStatement/CallableStatement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //请求参数,常用在preparedStatement用来设置相应的请求参数
    handler.parameterize(stmt);
    return stmt;
  }

小结

  1. BaseExecutor抽象类提供了对CRUD操作的入口,并带有缓存效应,子类只需要复写doUpdate()和doQuery()抽象方法即可

  2. SimpleExecutor-简单的处理实现类,即基本每次对相同的sql语句都会创建新的Statement对象;ReuseExecutor-复用处理实现类,即对相同的sql语句会缓存Statement对象;BatchExecutor-批处理实现类

  3. 最终获取Statement对象以及执行sql语句的解释权在于StatementHandler接口,详情看下节内容

转载于:https://www.cnblogs.com/question-sky/p/7353418.html

你可能感兴趣的:(Mybatis源码分析-BaseExecutor)