吐血整理Mybatis源碼的解析方式

Mybatis 源碼下載地址:https://github.com/mybatis/mybatis-3

Mybatis官方文檔地址:https://mybatis.org/mybatis-3/index.html

1,Mybatis的整體架構

在這裏插入圖片描述

2,Mybatis源碼思維導圖(畫質壓縮了,可私信要思維導圖)

在這裏插入圖片描述

3,mybatis三大核心流程:

在這裏插入圖片描述

4,Mybatis的初始化

在這裏插入圖片描述

(1)先閱讀官方問文檔

在這裏插入圖片描述

先導入mybatis的maven依賴

在這裏插入圖片描述

每個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的實例爲核心的。SqlSessionFactory 的實例可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置文件或一個預先定製的 Configuration 的實例構建出 SqlSessionFactory 的實例。
從 XML 文件中構建 SqlSessionFactory 的實例非常簡單,建議使用類路徑下的資源文件進行配置。 但是也可以使用任意的輸入流(InputStream)實例,包括字符串形式的文件路徑或者 file:// 的 URL 形式的文件路徑來配置。MyBatis 包含一個名叫 Resources 的工具類,它包含一些實用方法,可使從 classpath 或其他位置加載資源文件更加容易

在這裏插入圖片描述

XML 配置文件中包含了對 MyBatis 系統的核心設置,包含獲取數據庫連接實例的數據源(DataSource)和決定事務作用域和控制方式的事務管理器(TransactionManager)
(2)寫好測試demo,進入斷點調試

在這裏插入圖片描述

@Test
    public void test1() throws IOException {
        // 讀取mybatis的配置文件
        InputStream in = Resources.getResourceAsStream("config/mybatisconfig.xml");
        // 創建SqlSessionFactory對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        // 獲取session
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 動態代理模式獲取mapper實例
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        String  name = mapper.getUserNameById(1);
        System.out.println("test1==============>"+name);
    }
(3)初始化階段

首先進入SqlSessionFactoryBuilder.class

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      // 初始化XMLConfigBuilder對象,讀取mybatisconfig.xml裏面的配置
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

進入parse()方法

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("*[local-name()='configuration']"));
    return configuration;
  }

進入parseConfiguration(parser.evalNode("*[local-name()=‘configuration’]")),括號裏的把標籤就是mybatisconfig.xml配置文件裏面的標籤

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("*[local-name()='properties']"));
      Properties settings = settingsAsProperties(root.evalNode("*[local-name()='settings']"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("*[local-name()='typeAliases']"));
      pluginElement(root.evalNode("*[local-name()='plugins']"));
      objectFactoryElement(root.evalNode("*[local-name()='objectFactory']"));
      objectWrapperFactoryElement(root.evalNode("*[local-name()='objectWrapperFactory']"));
      reflectorFactoryElement(root.evalNode("*[local-name()='reflectorFactory']"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("*[local-name()='environments']"));
      databaseIdProviderElement(root.evalNode("*[local-name()='databaseIdProvider']"));
      typeHandlerElement(root.evalNode("*[local-name()='typeHandlers']"));
      //  解析<mappers>節點
      mapperElement(root.evalNode("*[local-name()='mappers']"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

上述源碼裏面的properties,settings等是解析mybatisconfig.xml配置文件裏面的標籤,如下圖:

在這裏插入圖片描述
再進入,propertiesElement(root.evalNode("*[local-name()=‘properties’]"));

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      if (resource != null) {
      // properties加載配置文件
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
      // 根據url加載配置文件
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      // 講配置文件中的信息與configuration裏面的variables進行合併
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      // 更新解析器parser和configuration裏面的variables
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

然後step out這個方法,進入mapperElement(root.evalNode("*[local-name()=‘mappers’]")),XMLMapperBuilder粉墨登場,用於解析mapper.xml

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
    // 獲取<mappers>子節點
      for (XNode child : parent.getChildren()) {
        // 是否有<package>子節點
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          // 獲取resource,url,class三個互斥子節點
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          // 如果resource子節點不爲空
          if (resource != null && url == null && mapperClass == null) {
          	 // 加載mapper映射文件
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 創建XMLMapperBuilder對象,用於解析mapper的映射文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
            // 如果url子節點不爲空
          } else if (resource == null && url != null && mapperClass == null) {			
          	// // 加載mapper映射文件
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            // 創建XMLMapperBuilder對象,用於解析mapper的映射文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
             // 如果class子節點不爲空
          } else if (resource == null && url == null && mapperClass != null) {			
          	// 加載class對象
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            // 向代理中心註冊mapper
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

mapperParser.parse();進入mapper解析器

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("*[local-name()='mapper']"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

進入configurationElement(parser.evalNode("*[local-name()=‘mapper’]")),開始解析mapper.xml文件,解析的是mapper.xml的節點而不是每一個sql裏面的節點

private void configurationElement(XNode context) {
    try {
      // 獲取命名空間namespace屬性
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      // 設置命名空間namespace屬性
      builderAssistant.setCurrentNamespace(namespace);
      // 解析cache-ref節點
      cacheRefElement(context.evalNode("*[local-name()='cache-ref']"));
      cacheElement(context.evalNode("*[local-name()='cache']"));
      // 解析parameterMap節點
      parameterMapElement(context.evalNodes("*[local-name()='parameterMap']"));
      // 解析 resultMap節點
      resultMapElements(context.evalNodes("*[local-name()='resultMap']"));
      sqlElement(context.evalNodes("*[local-name()='sql']"));
      // 解析select,update,insert,delete節點
      buildStatementFromContext(context.evalNodes("*[local-name()='select' or local-name()='insert' or local-name()='update' or local-name()='delete']"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

點擊進入buildStatementFromContext(context.evalNodes("*[local-name()=‘select’ or local-name()=‘insert’ or local-name()=‘update’ or local-name()=‘delete’]")),重點分析解析select,update,insert,delete節點

// 解析select,update,insert,delete節點
private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }
  // 解析所以sql語句節點 ,並註冊到configuration中
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      // 創建XMLStatementBuilder對象,專門用於解析sql語句節點
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
      	// 解析sql語句節點
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

進入解析sql語句節點方法statementParser.parseStatementNode();

public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
	// 用於解析對應每一個sql節點裏面的標籤屬性
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

至此mybatis初始化工作完成,配置文件的所有信息都被加載到了sqlSessionFactory中的configration對象內
在這裏插入圖片描述

(3)代理階段

在這裏插入圖片描述
如UserMapper mapper = sqlSession.getMapper(UserMapper.class),進入getMapper()

@Override
  public <T> T getMapper(Class<T> type) {
  	/*使用configuration對象得到mapper對象,
  	而configuration對象以初始化在內存中,
  	單例模式,生命週期貫穿整個應用週期*/
    return configuration.<T>getMapper(type, this);
  }

進入getMapper()方法裏面

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 	 // 使用動態代理的方式實現mapper接口,直接到invoke()反射內查看代碼
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 一個sqlSession對應一個接口方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

點擊進入mapperProxyFactory.newInstance(sqlSession)

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

MapperProxy創建對象,invoke()反射法動態代理生成mapper對象

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  // 記錄關聯的sqlSession對象
  private final SqlSession sqlSession;
  // mapper接口對應的class對象
  private final Class<T> mapperInterface;
  // key是mapper接口中某個方法的method對象,value對應的是MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 從緩存中獲取一個MapperMethod對象,如果緩存中沒有,就創建一個添加到緩存中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //調用excute方法執行sql
    return mapperMethod.execute(sqlSession, args);
  }

進入執行sql語句的方法mapperMethod.execute(sqlSession, args)

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 * @author Lasse Voss
 */
public class MapperMethod {
  // 從configuration中獲取方法的命名空間,方法名以及sql語句的類型
  private final SqlCommand command;
  // 封裝mapper接口方法的相關信息(入參。返回類型)
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 通過sql的類型,接口返回參數,選擇對應的INSERT...
    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:
      	// 查看method對象對返回結果類型的判斷
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
          // 返回集合或數組
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
          // 返回map
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
          // 返回遊標
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          // 返回單一對象的情況,通過參數解析器解析
          Object param = method.convertArgsToSqlCommandParam(args);
          // 查看select返回單個對象的底層實現
          result = sqlSession.selectOne(command.getName(), param);
        }
        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;
  }

進入new MethodSignature(config, mapperInterface, method),獲取封裝mapper的相關參數

public static class MethodSignature {
	// 返回相關參數類型的布爾值定義
    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final boolean returnsCursor;
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    // 該方法參數解析器
    private final ParamNameResolver paramNameResolver;

    public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      // 通過解析器解析該方法返回參數的類型
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
      } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
      } else {
        this.returnType = method.getReturnType();
      }
      // 初始化返回值字段
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
      this.returnsCursor = Cursor.class.equals(this.returnType);
      this.mapKey = getMapKey(method);
      this.returnsMap = (this.mapKey != null);
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }

再進入selectOne()方法查看底層實現

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

  @Override
  public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
    return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
  }

  @Override
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
    return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
  }

  @Override
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
    final List<? extends V> list = selectList(statement, parameter, rowBounds);
    final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
        configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
    final DefaultResultContext<V> context = new DefaultResultContext<V>();
    for (V o : list) {
      context.nextResultObject(o);
      mapResultHandler.handleResult(context);
    }
    return mapResultHandler.getMappedResults();
  }

由此可知不管是返回的單個對象,還是map,或是其他的,底層都是通過selectList()方法,返回list集合,最後做的轉換
在這裏插入圖片描述

sqlSession查詢接口嵌套關係:

在這裏插入圖片描述

(4)數據讀寫階段

Executor的三個重要組件

  • StatementHandler:它的作用是使用數據庫的Statement或PrepareStatement執行操作,啓承上啓下作用;
  • ParameterHandler:對預編譯的SQL語句進行參數設置
  • ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型;

接着上一段源碼,進入selelectList()方法

@Override
 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
   try {
     // 從configuration中獲取要執行sql語句執行信息
     MappedStatement ms = configuration.getMappedStatement(statement);
     // 此處進入數據庫階段,通過executor執行sql語句,並返回指定結果集
     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();
   }
 }

再進入query()方法

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  	// 獲取sql語句信息,包括佔位符,參數等信息
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 拼裝緩存的key值
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

  @SuppressWarnings("unchecked")
  @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());
    // 檢查exector是否關閉
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    // 非嵌套查詢並且FlushCache配置爲true,則要清空一級緩存
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      // 查詢層次加1
      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;
  }

進入真正訪問數據庫的方法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 {
      // 調用doQuery()方法並接收返回結果
      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;
  }

進入查詢的實現方法doQuery(),模板設計模式由子類實現3個方法,批量數據庫操作,緩存預編譯處理,默認處理器 (依次對應)
在這裏插入圖片描述

@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      // 獲取configuration對象
      Configuration configuration = ms.getConfiguration();
      // StatementHandler創建對象handler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 使用prepareStatement()預編譯對佔位符進行處理
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 通過StatmentHandler調用ResultSetHandler將結果集轉化爲指定對象返回
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

進入prepareStatement()預編譯處理

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
	// 創建Statement
    Statement stmt;
    // 通過動態代理獲取connect對象,並添加日誌功能
    Connection connection = getConnection(statementLog);
    // 通過不同的StatementHandler,利用connect創建Statement
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 使用parameterize()處理佔位符
    handler.parameterize(stmt);
    return stmt;
  }

parameterHandlder主鍵以及飢渴難耐,進入parameterize()處理佔位符

@Override
  public void parameterize(Statement statement) throws SQLException {
  	// parameterHandler主鍵開始進行佔位符處理
    parameterHandler.setParameters((PreparedStatement) statement);
  }

進入setParameters(),進入佔位符處理的具體業務

@Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 從boundSql中獲取sql語句佔位符參數信息
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        // 對於存儲過程的參數不處理
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          // 綁定的實參
          Object value;
           // 參數的名字
          String propertyName = parameterMapping.getProperty();
          // 獲取對應的實參值
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            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);
          }
          // 從parameterMapping中獲取typeHandler 
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          // 獲取對應參數的jdbcType
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

佔位符處理完畢開始執行sql,進入handler.query(stmt, resultHandler)

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    // 似曾相識的jdbc操作
    PreparedStatement ps = (PreparedStatement) statement;
    // 執行sql語句
    ps.execute();
    // resultSetHandler對sql結果進行處理
    return resultSetHandler.<E> handleResultSets(ps);
  }

進入resultSetHandler. handleResultSets(ps),查看sql返回結果處理

@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
	// 保存結果集的對象
    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    // statement肯返回多個結果集的對象,這裏先取第一個
    ResultSetWrapper rsw = getFirstResultSet(stmt);
	// 獲取結果集resultMap,本質就是獲取字段與java屬性的映射
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    // 獲取結果集不能爲空,爲空拋出異常
    while (rsw != null && resultMapCount > resultSetCount) {
      // 獲取當前結果集對應的resultMap
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 根據映射規則(resultMap)對結果集進行轉化,並放入multipleResults
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 獲取下一個結果集
      rsw = getNextResultSet(stmt);
      // 清空nestedResuletsetObject對象
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
	//獲取多結果集,多結果集多存在與存儲過程,返回多個resultSets,下次更新
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

Excutor內幕結構流程:

在這裏插入圖片描述
最後接上面測試demo運行結果:

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 2104545713.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7d70d1b1]
==>  Preparing: select name from t_user where id=? 
==> Parameters: 1(Integer)
<==    Columns: name
<==        Row: 張三
<==      Total: 1
test1==============>張三

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章