MyBatis源码分析

这篇文章咱么主要根据MyBatis的执行流程,通过Debug的方式,来一步步非常详细的带着大家看下MyBatis的从加载配置文件、解析配置文件、创建四大核心对象(Executor、ParameterHandler、ResultSetHandler、StatementHandler)的详细过程,以及在这个过程中是如何的设置参数?如何执行查询?如何封装结果集?同时会给大家介绍下Mybatis中常用的几个设计模如:工厂模式、建造者模式等。最终可以达到从模仿到超越。

环境准备

首先,创建一个最简单的Mybatis工程,只需要再pom文件中添加Mybatis的依赖就行。

/**
     * mybatis源码分析
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {
        // 1.加载配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 2. 创建SqlSessionFactory对象实际创建的是DefaultSqlSessionFactory对象
        SqlSessionFactory builder = new SqlSessionFactoryBuilder().build(inputStream);
        // 3. 创建SqlSession对象实际创建的是DefaultSqlSession对象
        SqlSession sqlSession = builder.openSession();
        // 4. 创建代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 5. 执行查询语句
        List<User> users = mapper.selectUserList();
        for (User user : users) {
            System.out.println(user);
        }
    }

第一步:加载配置文件

// 1.加载配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

DeBug一直往下,会进到一个ClassLoaderWrapper类中,并找到配置文件,最终返回一个BufferrdInput Stream带有缓冲的输入流,它继承于FilterIn putStream,第一步就只加载配置文件

第二步:创建SqlSessionFactory

接着进入第二步,这一步通过解析加载的配置文件,创建一个配置类Configuration,而且这一步中也把映射文件一并解析,最终返回DefaultSqlSessionFactory 对象,是一个工厂对象,工厂模式再Mybatis中的是相当多,以及建造者模式

 // 2. 创建SqlSessionFactory对象实际创建的是DefaultSqlSessionFactory对象
SqlSessionFactory builder = new SqlSessionFactoryBuilder().build(inputStream);

接着往下走进入

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 创建XMLConfigBuilder,用来解析xml配置文件,使用的是建造者模式
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 解析配置文件,构建SqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

最重要的是parser.parse()这个方法,这里面试真正解析核心配置文件的,进入这个方法中

public Configuration parse() {
    // 判断配置文件是否被解析过,一个配置文件呢只能被解析一次
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 解析核心配置文件的configuration节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

最终返回的是一个Configration对象,这个对象封装了所有的配置相关的信息,后面还会在看,这里可以看到

parseConfiguration(parser.evalNode("/configuration"));

这个代码已经开始解析配置文件的configuation节点,并且从上往下开始解析。我们进到这个方法中

 private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      // 解析properties节点
      propertiesElement(root.evalNode("properties"));
      // 解析settings并将其转换为 Properties 对象
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      // 加载 vfs虚拟文件系统
      loadCustomVfs(settings);
      // 解析settings
      loadCustomLogImpl(settings);
      // 解析typeAliases
      typeAliasesElement(root.evalNode("typeAliases"));
      // 解析plugins
      pluginElement(root.evalNode("plugins"));
      // 解析objectFactory
      objectFactoryElement(root.evalNode("objectFactory"));
      // 解析objectWrapperFactory
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      // 解析reflectorFactory
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      // settings 中的信息设置到 Configuration 对象中
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 解析environments
      environmentsElement(root.evalNode("environments"));
      // 解析 databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 解析mappers
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

至此,我们就看明白了,mybatis-config.xml核心配置文件的解析过程,其他的节点好比较好理解,跟所有的xml配置文件的解析都是一样的,一层一层的解析里面的子节点或属性,所以这里我们略过;重点要看的是关于mappers节点的解析,这个是要加载映射文件的关键地方

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
          // 解析扫描包的配置方式
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
            // 扫描包里的映射文件,最终放到一个map中备用,type作为key,创建的代理对象作为value
          configuration.addMappers(mapperPackage);
            // 其他配置方方式resource、url、class
        } else {
            // 解析reource的配置方式
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
              // 解析映射文件
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            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()

 public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        // 解析mapper节点
      configurationElement(parser.evalNode("/mapper"));
        // 添加的configura中的集合中
      configuration.addLoadedResource(resource);
        // 绑定命名空间
      bindMapperForNamespace();
    }

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

继续往下看

  private void configurationElement(XNode context) {
    try {
        // 拿到命名空间
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
        // 处理缓存的配置标签
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
        // 解析parameterMap
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
         // 解析resultMap
      resultMapElements(context.evalNodes("/mapper/resultMap"));
       // 解析 sql标签
      sqlElement(context.evalNodes("/mapper/sql"));
        // 解析增删改查标签
      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);
    }
  }

这个地方的作用是解析映射文件,和之前的解析核心配置文件的逻辑是一样的。不在赘述。重要的一个是解析增删改查标签的方法,跟着往下走

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        // 创建 XMLStatementBuilder
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
          // 解析sql标签
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

进来之后

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

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    // 解析<select>标签中个属性
    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);

    // 省略不必要的代码
    // ......
    // 解析好的属性设置倒MapperBuilderAssistant中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

这里的逻辑和之前的一样的,解析标签中的具体属性,进到这个方法addMappedStatement中

public MappedStatement addMappedStatement(
    // 构建一个MappedStatement对象
    MappedStatement statement = statementBuilder.build();
    // 最终设置到configuration的map中,key是标签的id,value是MappedStatement对象
    configuration.addMappedStatement(statement);
    // 返回MappedStatement
    return statement;
 }

添加到map中, 这里的的map是StrictMap,是继承了HashMap的在Configuration中的内部类

 public void addMappedStatement(MappedStatement ms) {
    // key是标签的id,value是MappedStatement对象
     mappedStatements.put(ms.getId(), ms);
  }

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

这里顺便说下,MyBatis中的很多用来存储参数映射、缓存等都是用的StrictMap

到此,mybatis解析核心配置文件和映射文件的过程就走完了,最后回到,开始的地方SqlSessionFactoryBuilder中的build(parser.parse())方法返回一个DefaultSqlSessionFactory对象

 public SqlSessionFactory build(Configuration config) {
    // 实际创建的是DefaultSqlSessionFactory对象
    return new DefaultSqlSessionFactory(config);
  }

到这里第二步构建DefaultSqlSessionFactory的过程就走完了。

第三步:创建SqlSession

接着我们看第三步, 通过上一步的工厂对象创建DefaultSqlSession,这个过程还会创建一个最重要的Executor对象

 // 3. 创建SqlSession对象实际创建的是DefaultSqlSession对象
 SqlSession sqlSession = builder.openSession();
  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);
        // 创建Executor
      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();
    }
  }

这里的Executor对象是MyBatis的核心对象,也是真正用来执行增删改查的对象,进到方法里

  • SimpleExecutor:简单执行器,没啥特殊,默认用它
  • CachingExecutor:缓存执行器,执行的也会用到,Mybatis的一级缓存是默认开启的
  • ReuseExecutor: 批处理执行器
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;
}

到此,这一步创建DefaultSqlSession和Executor的过程就完成了。

第四步:获取代理对象

获取代理对象之前,我们先回忆一下,在上一步解析mapper节点的时候,解析完成之后,有一个bindMapperForNamespace()方法

 private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
          // 获取命名空间,使用发射机制实例化对象
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
          // 判断是否已经存在绑定的命名空间
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResoue
          configuration.addLoadedResource("namespace:" + namespace);
            // mapper加载到对象的配置中
          configuration.addMapper(boundType);
        }
      }
    }
  }

跟着这个加载的方法addMapper()方法进去,这个方法是在MapperRegistry类中

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
          // 存储到集合中,key是type ,vaule就是对应的工厂类
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

这里的knownMappers和前面提到的mappedStatements都是一样的逻辑,放到map中,到这里明白了在解析命名空间的时候,就已经把mapper接口放到了map中,下面创建代理对象的内容就更好理解了

跟着第4不往下走,获取代理对象,断点往下走进入到MapperRegistry类中

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从上一步存入的map中获取代理工厂
    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代理工厂类创建mapper代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

进到这个newInstance看都了new了一个MapperProxy代理对象,调用自己MapperProxyFactory类自己的newInstance方法,

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
   // 创建代理对象
     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

这里就通过JDK的动态代理的方式创建了mapper代理对象,这里mapper代理对象的创建就像完成了。

第五步:执行查询

经过前面三部曲的准备,核心配置文件准备好了,映射文件准备好了,代理对象也有了;接下来,最后一步关键的步骤执行查询,这里也是比较复杂的地方,参数的封装,sql的预编译,结果集的封装等都是在这里处理的

接着进入到代理对象的查询方法,代理对象首先会先执行invoke方法

  @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);
     // 这对jdk8、9新特性做的处理
      } else if (method.isDefault()) {
         if (privateLookupInMethod == null) {
          return invokeDefaultMethodJava8(proxy, method, args);
        } else {
          return invokeDefaultMethodJava9(proxy, method, args);
        }
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
      // 从缓存中获取 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
      // 调用 execute 方法执行 SQL
    return mapperMethod.execute(sqlSession, args);
  }

执行execute的方法,之前会先判断下sql是哪种类型

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
   // 判断sql的类型
    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:
            // 无返回值
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
            // 咱么这里查询是返回多个结果
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
            // 返回map
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          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;
  }

接着进入到executeForMany方法中

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    // 参数处理
    Object param = method.convertArgsToSqlCommandParam(args);
  // 是否逻辑分页
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
    // 执行查询
        result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
        // 执行查询
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

这里的执行方法实际就是调用的前面所创建的DefaultSqlSession的方法继续往下走看到

  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 从配置类中获取MappedStatement对象
      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();
    }
  }

这里获取MappedStatement对象,就是前面存储到map中的,这里突然明白之前很对对象都存到map中,是备用

  public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 从MappedStatement获取BoundSql对象拿到获取绑定的sql,并将参数对象与sql语句的#{}对应
      BoundSql boundSql = ms.getBoundSql(parameterObject);
      // 创建缓存的key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
      // 执行查询
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

sql语句是在解析标签的时候,封装成对应的MappedStatement对象,其中标签里的sql就是封装成了sqlSource对象,这里的BoundSql实际是从绑定动态sql,继续执行查询,这里的查询其实用到的是CacheExecutor

 @Override
  public  List 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 list = (List) 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;
      }
    }
      // 执行查询的委托给了SimplExecutor对象
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

继续跟着源码进去,发现是进到了BaseExecutor里面

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) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
          // 缓存中没有去数据库查
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    }
    // 省略不必要代码
    return list;
  }

接下来就是去数据库查询了

private  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List 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;
  }

接着继续进入到doQuery()方法中

@Override
public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
        // 创建StatementHandler对象,这里是PreparedStatementHandler对象,进行预处理
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 预处理和jdbc的prepareStatement就是一样的
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行查询
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

在创建PreparedStatementHandler对象的过程中,还涉及到了插件、参数处理器(ParameterHandler)、结果处理器(ResultSetHandler),大家可以跟着断点进去看下,在预处理的阶段对参数进行了设置

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

在参数设置的时候使用的typeHandler进行设置

@Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List parameterMappings = boundSql.getParameterMappings();
        // 省略不必要代码...
          try {
              // 根据参数索引位置进行设置参数
              typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {

          }
        }
      }
    }
  }

接下来我们继续看查询的步骤

  @Override
  public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
   // 执行查询,这里就是jdbc的来具体的执行了
      ps.execute();
    // 处理结果
    return resultSetHandler.handleResultSets(ps);
  }

第六部:封装结果集

最后再来看下一下处理结果集的过程,Mybatis可以自动将查询的结果映射成实体类对象,这也是mybatis最大的一个特点


public List handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List multipleResults = new ArrayList<>();
    int resultSetCount = 0;

    /*获取第一个结果集*/

    ResultSetWrapper rsw = getFirstResultSet(stmt);
    List resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
    /*处理结果集 */
          ResultMap resultMap = resultMaps.get(resultSetCount);
          handleResultSet(rsw, resultMap, multipleResults, null);
         /* 获取下一个结果集 */
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
    }
    /* 获取多结果集,一般使用存储过程才有用到,这里不讨论,用的不多 */
    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++;
      }
    }
    return collapseSingleResultList(multipleResults);
  }

我们进到handleResultSet()的方法

// handleRowValues
 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
        //  处理嵌套查询结果集
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        // 处理简单查询结果
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

进到handleRowValuesForNestedResultMap()方法


  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
   // 处理逻辑分页用到这个方法
      skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
        // 获取结果
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      // 存储结果
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

进到getRowValue()方法中


private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 创建实体类对象
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
     // 进行自动映射
     foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 根据  节点中配置的映射关系进行映射
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

跟着进入到应用自动映射applyAutomaticMappings()的方法 继续跟进createAutomaticMappings()创建自动映射的方法


private List createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    final String mapKey = resultMap.getId() + ":" + columnPrefix;
    List autoMapping = autoMappingsCache.get(mapKey);
    if (autoMapping == null) {
      autoMapping = new ArrayList<>();
        // 加载未映射的属性
      final List unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
      for (String columnName : unmappedColumnNames) {
        String propertyName = columnName;
        if (columnPrefix != null && !columnPrefix.isEmpty()) {
          // When columnPrefix is specified,
          // ignore columns without the prefix.
          if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
            propertyName = columnName.substring(columnPrefix.length());
          } else {
            continue;
          }
        }
          // 获取属性
        final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
        if (property != null && metaObject.hasSetter(property)) {
          if (resultMap.getMappedProperties().contains(property)) {
            continue;
          }
          final Class propertyType = metaObject.getSetterType(property);
          if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
            final TypeHandler typeHandler = rsw.getTypeHandler(propertyType, columnName);
             // 封装上面获取到的信息到 UnMappedColumnAutoMapping 对象中
            autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
          } else {
            configuration.getAutoMappingUnknownColumnBehavior()
                .doAction(mappedStatement, columnName, property, propertyType);
          }
        } else {
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
        }
      }
        // 放入缓存
      autoMappingsCache.put(mapKey, autoMapping);
    }
    return autoMapping;
  }

最后回到getRowValue()方法,往下走进入到applyPropertyMappings()方法


private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    // 获取已映射的列名
    final List mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    // 获取 ResultMapping
    final List propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.getNestedResultMapId() != null) {
        // the user added a column attribute to a nested result map, ignore it
        column = null;
      }
      if (propertyMapping.isCompositeResult()
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
          || propertyMapping.getResultSet() != null) {
          // 从结果集中获取指定列的数据
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue make property optional
        final String property = propertyMapping.getProperty();
        if (property == null) {
          continue;
        } else if (value == DEFERRED) {
          foundValues = true;
          continue;
        }
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
          // gcode issue, call setter on nulls (value is not 'found')
            // 将获取到的值设置到实体类对象中
          metaObject.setValue(property, value);
        }
      }
    }
    // 返回结果
    return foundValues;
  }

至此,结果集的封装结束

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注