天天看點

Mybatis源碼分析 v1.0

第一節 參考

一.參考資料

https://github.com/mybatis/mybatis-3

mybatis的單元測試用的hsqldb資料庫,需要自己建個單元測試用例, 連接配接mysql.我的測試用例如下圖所示.

Mybatis源碼分析 v1.0
Mybatis源碼分析 v1.0

第二節 架構

一.分層

核心原理三步走:

1.啟動讀取xml配置,解析到Configuration類中,以Configuration為構造方法參數建立SqlSessionFactory

2.通過DefaultSqlSessionFactory類建立DefaultSqlSession,通過DefaultSqlSession擷取到mapper接口的代理類.

3.通過mapper接口的代理類執行sql語句

具體過程:

Configuration+代理+指令模式+委托模式。

所有的mybatis配置讀取xml到Configuration類中. 然後調用MapperProxyFactory類,生成所有mapper接口類的代理類MapperProxy.在MapperProxy類的invoke方法中,調用MapperMethod.execute方法中執行各種指令.具體指令由RoutingStatementHandler委托具體的類處理,執行由STATEMENT,PREPARED,CALLABLE類型的具體類型處理.

二.SqlSessionFactoryBuilder/SqlSessionFactory

三.SqlSession

四.Configuration

五.Executor

六.BoundSql

七.StatementHandler

八.ResultSetHandler

第三節 源碼細節

一.啟動讀取xml配置,建立SqlSessionFactory

調用SqlSessionFactoryBuilder#build(Reader)使用builder模式建立建立SqlSessionFactory.入參是是xml檔案生成的Reader.主要是填充Configuration類對象,然後把這個對象作為DefaultSqlSessionFactory構造方法的參數.

xml檔案使用sax庫解析到XMLConfigBuilder類中.然後進入XMLConfigBuilder#parseConfiguration(XNode)解析到Configuration類型的BaseBuilder#configuration成員中.XMLConfigBuilder類繼承自BaseBuilder類,BaseBuilder類中成員如下:

public abstract class BaseBuilder {
  //xml中的各種配置
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;
}
           

1.解析xml内容的方法XMLConfigBuilder#parseConfiguration()代碼如下:

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      /* 解析properties标簽中的name value值存儲到Properties類型的Configuration#variables成員中
      比如示例代碼中的<property name="dialect" value="mysql" /> */
      propertiesElement(root.evalNode("properties"));
      /* 解析setting标簽為Properties格式,比如示例中的<setting name="mapUnderscoreToCamelCase" value="true" /> */
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //解析VFS的子類放到Configuration#vfsImpl成員中,沒用過
      loadCustomVfs(settings);
      //解析Log接口的實作類放到Configuration#logImpl成員中,沒用過
      loadCustomLogImpl(settings);
      /* 解析typeAliases,調用TypeAliasRegistry#registerAliases()注冊類型别名到TypeAliasRegistry#TYPE_ALIASES成員Map中*/
      typeAliasesElement(root.evalNode("typeAliases"));
      /*注冊plugins插件到類型為InterceptorChain的Configuration#interceptorChain中*/
      pluginElement(root.evalNode("plugins"));
      /*解析objectFactory到類型為ObjectFactory的Configuration#objectFactory成員中*/
      objectFactoryElement(root.evalNode("objectFactory"));
      /*解析objectWrapperFactory到類型為ObjectWrapperFactory的成員Configuration#objectWrapperFactory中*/
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      /*解析reflectorFactory标簽到類型為ReflectorFactory的成員Configuration#reflectorFactory中*/
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      /*解析setting标簽到configuration成員中,具體支援的設定項入下面代碼2處所示.*/
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      /*解析environments标簽到類型為Environment的成員Configuration#environment中這裡面存放了jdbc連接配接的配置,Environment類成員在下面代碼給出*/
      environmentsElement(root.evalNode("environments"));
      /*解析databaseIdProvider标簽的值到Configuration#databaseId*/
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /*解析typeHandlers到TypeHandlerRegistry#TYPE_HANDLER_MAP的成員map中*/
      typeHandlerElement(root.evalNode("typeHandlers"));
      /*注冊所有xml的mapper檔案,内部邏輯是針對每個xml檔案,建立XMLMapperBuilder對象,調用XMLMapperBuilder#parse()解析mapper檔案,代碼在後面3處分析*/
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}
           

Environment類成員:

public final class Environment {
  private final String id;
  private final TransactionFactory transactionFactory;
  private final DataSource dataSource;
}
           

2.mybatis支援的setting配置項

進入XMLConfigBuilder#settingsElement()方法,代碼如下:

private void settingsElement(Properties props) {
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
           

3.解析mapper的xml檔案

進入XMLMapperBuilder#parse(),代碼如下:

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      /*代碼在下面分析*/
      configurationElement(parser.evalNode("/mapper"));
      //把目前的mapper檔案加到Configuration#loadedResources的set中,代表已經加載過
      configuration.addLoadedResource(resource);
      /* 把目前解析的mapper對應的class加到MapperRegistry#knownMappers的成員中,key是Class對象,value是MapperProxyFactory類型,這個MapperProxyFactory是直接new出來的泛型類對象,泛型指向mapper檔案對應的Class接口類型,比如xxx.UserMapper.然後調用MapperAnnotationBuilder#parse()方法解析Mapper檔案對應的Class接口中的每個方法上面的注解。這種在接口方法上面通過注解寫sql語句的方法比較少用,不友善.*/
      bindMapperForNamespace();
    }
    //解析之前不完整的ResultMap标簽,示例代碼沒有
    parsePendingResultMaps();
    //解析之前不完整的cacheref标簽,示例代碼沒有
    parsePendingCacheRefs();
    //解析之前不完整的statement語句,示例代碼沒有
    parsePendingStatements();
}
           

解析mapper檔案标簽

private void configurationElement(XNode context) {
    try {
      //擷取namespace,比如com.xx.UserMapper接口,成員方法對應xml的每個sql語句
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      //解析cache-ref标簽到Configuration#cacheRefMap中
      cacheRefElement(context.evalNode("cache-ref"));
      //解析cache緩存标簽到Configuration#caches成員中.
      cacheElement(context.evalNode("cache"));
      //解析parameterMap标簽到Configuration#parameterMaps成員中.
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      /*resultMap代表damain對象中字段,類型和資料庫列的一一映射關系.解析到Configuration#resultMaps成員map中,key是id,value是個ResultMap類.這個類的成員在下面4給出.*/
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      /*周遊所有的sql片段語句到XMLMapperBuilder#sqlFragments的map中.key是sql的id.value是XNode格式.*/
      sqlElement(context.evalNodes("/mapper/sql"));
      /*周遊所有的sql語句,解析到Configuration#mappedStatements的map中,key是id,value是MappedStatement類,代表一個sql語句,它的類成員在後面4給出.*/
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
}
           

4.存儲xml标簽和sql語句的一些資料類.

ResultMap類,代表domain對象和資料庫表的列的映射關系,代碼如下:

public class ResultMap {
  private Configuration configuration;

  private String id;
  private Class<?> type;
  private List<ResultMapping> resultMappings;
  private List<ResultMapping> idResultMappings;
  private List<ResultMapping> constructorResultMappings;
  private List<ResultMapping> propertyResultMappings;
  private Set<String> mappedColumns;
  private Set<String> mappedProperties;
  private Discriminator discriminator;
  private boolean hasNestedResultMaps;
  private boolean hasNestedQueries;
  private Boolean autoMapping;
}
           

代表一個mapper中select|insert|update|delete的sql語句的類.代碼如下:

public final class MappedStatement {
  //xml檔案的全路徑
  private String resource;
  private Configuration configuration;
  //sql語句的全路徑,比如com.xxx.UserMapper.selectByExample
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  /*語句類型,枚舉值enum StatementType {
  		STATEMENT, PREPARED, CALLABLE
	}, 預設是PREPARED枚舉*/
  private StatementType statementType;
  //預設是Default枚舉
  private ResultSetType resultSetType;
  //原始的sql語句,預設是DynamicSqlSource類型,内有SqlNode類型的rootSqlNode成員存儲sql語句.在後面給出SqlNode類成員.
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  /*sql指令類型,枚舉值 enum SqlCommandType {
  		UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
	}
	*/
  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;
}
           

存儲Sql語句的遞歸結構

public class MixedSqlNode implements SqlNode {
  private final List<SqlNode> contents;
}
           

二.通過DefaultSqlSessionFactory類擷取到mapper接口的代理類.

1.DefaultSqlSessionFactory#openSession()擷取SqlSession對象.進入DefaultSqlSessionFactory#openSessionFromDataSource()方法建立SqlSession.代碼如下:

參數ExecutorType是枚舉類型,預設是SIMPLE.

public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}
           

參數TransactionIsolationLevel隔離級别也是枚舉.

public enum TransactionIsolationLevel {
  NONE(Connection.TRANSACTION_NONE),
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
}
           
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //擷取xml檔案中配置的jdbc連接配接參數.
      final Environment environment = configuration.getEnvironment();
      //擷取事務工廠為JdbcTransactionFactory類
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //建立一個Transaction事務對象,jdbc的為JdbcTransaction.類成員在後面2給出
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //預設建立SimpleExecutor執行器.作為CachingExecutor緩存執行器的成員,委托模式.
      //如果有Interceptor,在這裡InterceptorChain#interceptors成員添加攔截這個CachingExecutor執行器.
      final Executor executor = configuration.newExecutor(tx, execType);
      //建立DefaultSqlSession對象傳回,DefaultSqlSession成員在後面2給出
      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();
    }
}
           

2.Jdbc事務對象

public class JdbcTransaction implements Transaction {
  //事務所在連接配接
  protected Connection connection;
  //資料源,連接配接的資料庫位址,密碼等
  protected DataSource dataSource;
  //事務隔離級别
  protected TransactionIsolationLevel level;
  //預設為false,不自動送出
  protected boolean autoCommit;
}
           

預設的Sql會話類

public class DefaultSqlSession implements SqlSession {
  private final Configuration configuration;
  //執行器,預設CachingExecutor中引用SimpleExecutor.
  private final Executor executor;
  //預設為false
  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;
}
           

3.通過DefaultSqlSession擷取mapper接口的代理類

進入DefaultSqlSession#getMapper(),參數為xxx.UserMapper的接口Class.實際調用成員Configuration#mapperRegistry的getMapper方法,mapperRegistry的填充在前面一.3中分析了.MapperRegistry#knownMappers的key是Class對象,value是MapperProxyFactory類型,這個MapperProxyFactory是直接new出來的泛型類對象,泛型指向mapper檔案對應的Class接口類型,比如xxx.UserMapper代碼如下:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	//擷取MapperProxyFactory泛型類.
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //建立mapper接口的代理對象,代碼如下所示
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}
           
public T newInstance(SqlSession sqlSession) {
	//MapperProxy實作了InvocationHandler接口,是代理類,所有這個mapper的sql語句都調用它的invoke方法
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    //代碼在下面,調用Proxy.newProxyInstance建立代理類,代理類調用MapperProxy類.
    return newInstance(mapperProxy);
}
           

建立動态代理

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
           

mapper接口代理類

public class MapperProxy<T> implements InvocationHandler, Serializable {
  private final SqlSession sqlSession;
  //代理的mapper接口
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  //sql語句代理方法
   @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //代理類的正常想法,去掉Object的方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        //如果是靜态,abstract方法,調用原對象
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    /*取出緩存MapperProxy#methodCache中的MapperMethod對象,開始是空的.建立MapperMethod對象後放進去,MapperMethod類成員下面給出*/
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //執行sql語句
    return mapperMethod.execute(sqlSession, args);
  }
}
           
public class MapperMethod {
  //方法對應的sql指令,包含方法名全路徑,方法類型
  private final SqlCommand command;
  //方法簽名,在下面給出類成員
  private final MethodSignature method;
}
           

方法簽名類

public static class MethodSignature {
	//是否傳回多條記錄
	private final boolean returnsMany;
	private final boolean returnsMap;
	private final boolean returnsVoid;
	private final boolean returnsCursor;
	private final boolean returnsOptional;
	//傳回類型
	private final Class<?> returnType;
	private final String mapKey;
	private final Integer resultHandlerIndex;
	private final Integer rowBoundsIndex;
	private final ParamNameResolver paramNameResolver;
}
           

三.通過mapper接口的代理類執行sql語句.

1.比如執行Employee employee = employeeMapper.selectByPrimaryKey(id)語句.

通過上面二.3的最後分析,進入MapperProxy#invoke()方法,調用MapperMethod#execute().通過指令模式實作,代碼如下:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
      	//根據select的傳回多條記錄,一條記錄,還是map等調用不同方法.
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          //上面的selectByPrimaryKey(id)測試代碼進入這裡.
          //把傳進來的sql語句的參數轉成Map<String, Object>類型的param
          Object param = method.convertArgsToSqlCommandParam(args);
          //執行sql查詢,測試代碼進入DefaultSqlSession#selectOne()方法,代碼在後面2處分析,政策模式
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional() &&
              (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
}
           

2.select類型的sql查詢

如果隻傳回一條記錄,進入DefaultSqlSession#selectOne().調用DefaultSqlSession#selectList(),代碼如下:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      /* 取出xml填充到Configuration#mappedStatements中的sql語句.這個填充過程在前面一.3處分析過了.MappedStatement類成員代碼在前面的一.4中有。具體的sql語句在MappedStatement#sqlSource成員中遞歸存儲*/
      MappedStatement ms = configuration.getMappedStatement(statement);
      //調用CachingExecutor#query(),代碼在下面分析
      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();
    }
}
           

緩存執行器執行查詢CachingExecutor#query(),代碼如下:

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
	//建立動态sql
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //緩存sql語句,key為為xml中的sql語句id+offset+limit+sql語句
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    //調用代碼在下面
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
           

動态sql類BoundSql成員,裡面有?,#{id}等占位符

public class BoundSql {
  //存放字元串形式的具體的sql語句
  private final String sql;
  private final List<ParameterMapping> parameterMappings;
  private final Object parameterObject;
  private final Map<String, Object> additionalParameters;
  private final MetaObject metaParameters;
}
           

緩存執行器執行查詢CachingExecutor#query(),代碼如下:

public <E> List<E> 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<E> list = (List<E>) 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;
      }
    }
    /* 沒有緩存,調用BaseExecutor#query()方法執行,進入BaseExecutor#queryFromDatabase(),代碼在下面分析*/
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
           

BaseExecutor#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 {
      //調用SimpleExecutor#doQuery()執行查詢.在後面的3處分析
      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;
}
           

3. SimpleExecutor#doQuery()執行查詢.代碼如下:

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();
      /* 建立RoutingStatementHandler執行sql.加入StatementHandler的目的是用來區分StatementType枚舉的三種調用STATEMENT, PREPARED, CALLABLE.如下面的RoutingStatementHandler類的構造方法所示,每種枚舉對應不同的StatementHandler*/
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //調用SimpleExecutor#prepareStatement()準備sql語句,準備過程在下面分析
      stmt = prepareStatement(handler, ms.getStatementLog());
      //調用jdbc執行sql查詢,代碼在後面分析
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
}
           

RoutingStatementHandler建立不同的種類的StatementHandler.内部成員delegate委托模式具體執行sql語句.

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

準備sql語句,進入SimpleExecutor#prepareStatement()

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //調用JdbcTransaction#openConnection(),進入javax.sql.DataSource#getConnection()擷取資料庫連接配接
    Connection connection = getConnection(statementLog);
    //處理sql語句的占位符
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}
           

調用jdbc執行sql查詢,向mysql通過socket發出sql語句.進入SimpleStatementHandler#query,如下代碼所示:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    //調用jdbc的方法java.sql.Statement#execute(java.lang.String)執行sql語句
    statement.execute(sql);
    return resultSetHandler.handleResultSets(statement);
}