mybatis执行器 - 969251639/study GitHub Wiki

首先,看下mybatis的查询是怎么操作流程的
在DefaultSqlSession方法中早到selectOne方法

@Override
  public <T> T selectOne(String statement) {
    return this.<T>selectOne(statement, null);
  }

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

可以看到selectOne和selectList是一样的,selectOne只是返回了List中第一个元素(也只能有一个,否则抛出TooManyResultsException异常)

@Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      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();
    }
  }

mybatis在查询之前需要获取Mapper,因为具体的sql定义是被封装到Mapper中的也就是MappedStatement对象

public final class MappedStatement {

  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
   ...
}

可以看到MappedStatement中的属性就是映射mapper xml中配置项

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="test.Blog" >
   <select id="findById"  resultType="test.Blog" parameterType="int" >  
      SELECT * FROM blog  WHERE id=#{id}
   </select>  
   <select id="add"  parameterType="test.Blog" >  
      INSERT INTO blog(name, type, create_time) VALUES('myblog', 1, now())
   </select>  
</mapper>

注:mapper在mybatis启动时就已经创建好了的,基本都是缓存中进行操作

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

  public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
    if (validateIncompleteStatements) {
      buildAllStatements();
    }
    return mappedStatements.get(id);
  }

获取到MappedStatement 之后就基本知道sql的样貌了,包括它的参数信息,返回信息等,然后调用执行器进行操作

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

默认的执行器是SimpleExecutor,执行器是在打开会话时创建的

final Executor executor = configuration.newExecutor(tx, execType);//获取一个sql执行器

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

另外有一个特殊的执行器是CachingExecutor,也就是mybtais的二级缓存,它是对三个基础执行器(BATCH,REUSE,SIMPLE)的包装,主要用于缓存处理,最后将其加入到plugin链中,用于mybatis的扩展。

SIMPLE就是一个sql查询器,封装了最基础的CRUD
BATCH顾名思义,主要用来操作批量操作,内部将sql收集到list中,然后一起发送给数据库处理

最终,获取到执行器之后就可以做真正的数据库操作了

⚠️ **GitHub.com Fallback** ⚠️