Mybatis 源 码 初 解 析 (三) 完

紧着着上文提到的,要来寻找第三部分,sql语句究竟是怎么执行的。
在此之前我要先弄清楚一些问题。
在构造sqlseesion的时候我们清楚的记得穿进去几个参数,包括executor,configuration,autocommit等的。
关于executor,要进行一下剖析,这个东西。执行器。
点开Executor

public interface Executor {
    
    

  ResultHandler NO_RESULT_HANDLER = null;

  int update(MappedStatement ms, Object parameter) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

  List<BatchResult> flushStatements() throws SQLException;

  void commit(boolean required) throws SQLException;

  void rollback(boolean required) throws SQLException;

  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

  boolean isCached(MappedStatement ms, CacheKey key);

  void clearLocalCache();

  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);

  Transaction getTransaction();

  void close(boolean forceRollback);

  boolean isClosed();

  void setExecutorWrapper(Executor executor);

}

清楚看到这是一个接口,然后我们看到这个接口的一些实现关系图。
在这里插入图片描述
接到上文 构造executor

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    
    
    Transaction tx = null;
    try {
    
    
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
    
    
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
    
    
      ErrorContext.instance().reset();
    }
  }

很明显在构造sqlseesion时先构造了一个executor,点进方法去看

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    
    
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
    
    
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
    
    
      executor = new ReuseExecutor(this, transaction);
    } else {
    
    
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
    
    
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

发现有三种定义好的type,而且有一个cacheEnabled字段,就是说这个字段是true才会new,这个字段就是默认设置的缓存开启

public enum ExecutorType {
    
    
  SIMPLE, REUSE, BATCH
}

所以自然而然有三种executor,他们都继承与baseExecutor
而默认调用的是simpleexecutor 看一下这个类

/*
 *    Copyright 2009-2021 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

/**
 * @author Clinton Begin
 */
public class SimpleExecutor extends BaseExecutor {
    
    

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    
    
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    
    
    Statement stmt = null;
    try {
    
    
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
    
    
      closeStatement(stmt);
    }
  }

  @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.query(stmt, resultHandler);
    } finally {
    
    
      closeStatement(stmt);
    }
  }

  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
    
    
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    Cursor<E> cursor = handler.queryCursor(stmt);
    stmt.closeOnCompletion();
    return cursor;
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) {
    
    
    return Collections.emptyList();
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    
    
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

}

里面就有很多我们熟悉的方法了,doquery,doupdate,connection等一系列jdbc的东西。所以合理猜测我们要验证的那部分很大可能与这个有关。
言归正转,我们第一部分,通过xmlconfigbuilder的parseConfiguration 解析 config.xml 并将解析到的值放入到configuration类中,获得与数据库的关联。
然后通过configuration类 build了一个默认的sqlsessionfactory, 然后opensession 通过executor,autocommit,configuration 等作为参数,构造了一个默认的sqlsession,然后通过sqlsession .getmapper (configuration.getmapper—>mapperregister.getmapper—>getmapper())获得mapperProxy的一个代理实例,然后invoke方法里面调用了execute的方法,里面通过关键词匹配进而执行了selectone方法,然后往下有一个getBoundSql获得了sql语句。
接下来就是要验证怎么去执行这个sql语句。 不用往下看,就应该明白完成这些事情的一定是executor,默认的为simpleexecutor

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    
    
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

获取到sql时,createCacheKey这个方法显然是在建立一个缓存key,因为我们说mybatis一级缓存是默认开启的。上面也有提到原因,在源码里面有了体现。
看下这个方法

  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    
    
    if (closed) {
    
    
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
    
    
      if (parameterMapping.getMode() != ParameterMode.OUT) {
    
    
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
    
    
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
    
    
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
    
    
          value = parameterObject;
        } else {
    
    
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
    
    
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

显然就是把刚刚boundsql里的一些内容存到一个新的cachekey的实例里面,往下的query方法:

 @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;
  }

很容易理解,通过key来查询一下localcache是否有东西,如果有的话直接从localcache里获得,

  private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
    
    
    if (ms.getStatementType() == StatementType.CALLABLE) {
    
    
      final Object cachedParameter = localOutputParameterCache.getObject(key);
      if (cachedParameter != null && parameter != null) {
    
    
        final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
        final MetaObject metaParameter = configuration.newMetaObject(parameter);
        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
    
    
          if (parameterMapping.getMode() != ParameterMode.IN) {
    
    
            final String parameterName = parameterMapping.getProperty();
            final Object cachedValue = metaCachedParameter.getValue(parameterName);
            metaParameter.setValue(parameterName, cachedValue);
          }
        }
      }
    }
  }

如果没有那么就走另一条路,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;
  }

先把这个key存入到localcache里,因为马上要进行一个doQuery操作,并且把doQuery查到的东西和key进行一个匹配一起存入到localCache中。显然这个doQuery就是执行了一个sql语句。
在此之前,先看一下何为localCache,根据猜测他就是一个hashmap,满足kv结构嘛。点进去一看是一个PerpetualCache类,

public class PerpetualCache implements Cache {
    
    

  private final String id;

  private final Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    
    
    this.id = id;
  }

  @Override
  public String getId() {
    
    
    return id;
  }

  @Override
  public int getSize() {
    
    
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    
    
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    
    
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    
    
    return cache.remove(key);
  }

  @Override
  public void clear() {
    
    
    cache.clear();
  }

  @Override
  public boolean equals(Object o) {
    
    
    if (getId() == null) {
    
    
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
    
    
      return true;
    }
    if (!(o instanceof Cache)) {
    
    
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    
    
    if (getId() == null) {
    
    
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }

}

言归正传,让我们继续去看doQuery方法,来自于BaseExecutor的方法,我们知道默认是进行的一个simpleExecutor里的方法,

  @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.query(stmt, resultHandler);
    } finally {
    
    
      closeStatement(stmt);
    }
  }

出现了StatementHandler 这是什么,statement sql对象啊,和jdbc联系起来了 吼吼吼~~小兴奋呢

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    
    
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

prepareStatement 字面看起来就是一个准备好的statement的,实际上就是一个预编译的sql语句对象,可以用来防止sql注入。
然后是什么,handler.query(stmt, resultHandler); 进行一个查询 result的出现,那不就是结果集嘛
点进去,依然是org.apache.ibatis.executor.statement.SimpleStatementHandler simple的

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    
    
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.handleResultSets(statement);
  }

从boundsql里拿到sql,用statement执行sql,返回结果集,jdbc的代码出现了。

至此一次完成的mybatis的运行流程大概结束了,并且验证我最初的猜想,而且执行sql语句的任务往大了说其实就是交给了sqlseesion。

这只是一次简单的源码解析,包括很多细节都没有去细究,包括二级缓存如何开启等待一系列的问题,还有batchexecutor,和refuseexecutor什么时候调用等都没有进行一个仔细探究。

但是就是这一次简单的阅读我也明白了优秀框架所具有的优势,以及很多代码上的启发

猜你喜欢

转载自blog.csdn.net/Yoke______/article/details/123970083