吐血整理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==============>张三

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