mybatis源码,SQL是如何执行的

1.这里只是讲述CRUD的执行,在CRUD之前,你服务启动的时候,mybtis会把mapper和xml中的节点SQL通过反射加载到Configuration中,这个不做描述了,后面再补;

2.我们直接开始启动工程跟着代码一点点跟,看是如何执行的;
3.我的springframework版本是5.3.7 mybatis版本3.4.6 springboot版本2.5.0 jdk1.8;
注意:当版本不同时,执行的逻辑肯定是不同的,因为spring和mybatis都会进行不断的优化升级

当我们执行一个SQL的时候,会先通过org.springframework.aop.support包中的AopUtils类,来执行处理,调用了方法invokeJoinpointUsingReflection
在这里插入图片描述

这里入参有三个,贴下入参在这里插入图片描述
1.可以看到这里的入参target是MapperProxy类,method是你的SQL方法,args是你的请求入参

2.invokeJoinpointUsingReflection 方法里面,主要有两行代码

// 这里面就做了一件事情使用 method.setAccessible(true) 改变了访问权限
ReflectionUtils.makeAccessible(method);
// 这个里面invoke了一堆代码,主要看这里
return method.invoke(target, args);

这里可以看到invoke会进入到Java Method类中的invoke,我们主要看mybatis就不介绍java的这些方法了(有的有点复杂,我也没看懂),这里可以看到进来之后继续到底下执行invoke,这个时候是调用的DelegatingMethodAccessorImpl类中的invoke来进行的执行这里是引用

可以看到DelegatingMethodAccessorImpl类中,这里的invoke是调用了MethodAccessorImpl的invoke,这里实际指向的对象是NativeMethodAccessorImpl这个类
这里是引用
可以看到这里最后跑到了NativeMethodAccessorImpl类中,执行的invoke0,invoke0方法是用native修饰的,说明这是调用的底层非java方法,调用的C,C++写的东西了(我也看不懂底层到底干了啥,JAVA设计的真负责,C我也木有学习过)在这里插入图片描述

接下来我们终于可以看框架源码了,框架看起来还是比较简单的,没java那么复杂了,我们可以看到这里最终调用的是MapperProxy类中的invoke方法,里面入参有三个,就是上面介绍invokeJoinpointUsingReflection方法的三个入参,一摸一样
在这里插入图片描述

  // 代码直接贴出来吧,这里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);
        // 这里跳到连接【https://blog.csdn.net/qq_43555164/article/details/108254738】看下原因
      } else if (isDefaultMethod(method)) {
        // 调用默认方法,这里面用到了Java7的API,有兴趣的读者可以自行了解
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 大多数情况我们会走到这里,从缓存中得到mapperMethod,没有的话会放的缓存里
    // 具体代码下面详细讲述,这里写的还挺多的
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 作用:执行
    return mapperMethod.execute(sqlSession, args);
  }

  // cachedMapperMethod方法,入参是你的method
  private MapperMethod cachedMapperMethod(Method method) {
    // 这里先根据methodCache拿,看能不能拿出来,methodCache是一个Map
    // private final Map<Method, MapperMethod> methodCache;
    MapperMethod mapperMethod = methodCache.get(method);
    // 为空就去new对象去了
    if (mapperMethod == null) {
    // 作用:构造MapperMethod,别小看这一行代码,里面写了一堆(我yue你仙人,我还以为就new了个对象,写那么复杂干啥?脑子被门挤了吗?)
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      // 把这俩玩意丢的map缓存起来
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }




--------------------------------------------华丽的分割线--------------------------------------------
    // 这里介绍下new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); 我再yue你一次仙人,G2加油,干翻T1
    // 这里实例化的时候会给对象里面的俩属性new一下,new的底下这两个东西
    // private final SqlCommand command;
    // private final MethodSignature method;
    // 介绍下三个入参
    //   mapperInterface: 这个就是你的mapper接口
    //   method: 这个是你执行的mapper里面的哪个SQL方法
    //   config: mybatis初始化的时候,把一堆东西丢config缓存里面了
    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // 这里new对象里面写的东西有点多,底下贴吧
    // 最终获取执行的SQL是CRUD的那种 && 执行的哪个SQL方法
    this.command = new SqlCommand(config, mapperInterface, method);
    // 这里new对象里面写的东西有点多,底下贴吧
    // 这个里面干了一堆事情(CV的其他人的解释:构造方法签名对象,具体的自己看源码)
    this.method = new MethodSignature(config, mapperInterface, method);
  }
  

可以看到这个SqlCommand对象,是在MapperMethod类里面的,还是个public类,估计那会想的能给其他地方也用

在这里插入图片描述

可以看到这里面写了一堆东西,接下来贴代码,看看这里面干了点啥
跟完源码我们会发现,其实这个地方就是校验了一下参数,顺便从缓存里面拿到了要执行的SQL对应的name和type
里面还调用了很多configuration类的方法来做判断和取值

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      // 获取method的名字的 我这里方法名字是【queryUser】
      final String methodName = method.getName();
      // 获取method是属于哪个接口的 我这里是【interface com.example.demo.Mapper.UserMapper】
      final Class<?> declaringClass = method.getDeclaringClass();
      // 调用了私有方法,又拿了个对象,看看这里面干了点啥,底下贴代码 G2牛逼,干翻了T1,nice
      // 这里下面跟了源码,主要就是从缓存map里面拿到对应的MappedStatement对象的,MappedStatement对象存储了很多的东西,包括你的资源,mapper,CRUD的哪一种等等
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
          configuration);
      // 判断对象是否为空,为空走这个逻辑,我们非空,后面补
      if (ms == null) {
        if (method.getAnnotation(Flush.class) != null) {
          name = null;
          type = SqlCommandType.FLUSH;
        } else {
          throw new BindingException("Invalid bound statement (not found): "
              + mapperInterface.getName() + "." + methodName);
        }
      } else {
        // 给本类的私有属性,name 赋值,我这里是【com.example.demo.Mapper.UserMapper.queryUser】
        name = ms.getId();
        // 给本类的私有属性,type赋值,我这里是【SELECT】
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }

	// 这里源码的私有方法,入参四个,上面都讲了,这里不再讲了
	// 需要注意【mapperInterface】和【declaringClass】,虽然我感觉是一摸一样,但是应该是有区别的(我现在还没发现哪里有区别)
    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
        Class<?> declaringClass, Configuration configuration) {
      // 拿到俩name组成一个ID,我的是【com.example.demo.Mapper.UserMapper.queryUser】
      String statementId = mapperInterface.getName() + "." + methodName;
      // 这里configuration参数的作用,判断了下是否有这个ID和返回对应的缓存中的对象,底下看下这个方法干了点啥
      // 找不到的话自己搜下hasStatement这个方法,底下介绍了这里就是判断缓存里面是否包含了这个方法
      if (configuration.hasStatement(statementId)) {
        // 包含的话就返回对应的MappedStatement对象
        return configuration.getMappedStatement(statementId);
        // 下面的这三个return情况,后面补
      } else if (mapperInterface.equals(declaringClass)) {
        return null;
      }
      for (Class<?> superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
          MappedStatement ms = resolveMappedStatement(superInterface, methodName,
              declaringClass, configuration);
          if (ms != null) {
            return ms;
          }
        }
      }
      return null;
    }

         /*##########我是华丽的分割线##########*/
  // 需要注意下这个俩方法在configuration类里面
  public boolean hasStatement(String statementName) {
  	// 调了内部的方法hasStatement
    return hasStatement(statementName, true);
  }
  // 俩入参
  // statementName : 我这里的这个参数是【com.example.demo.Mapper.UserMapper.queryUser】,就是外面调用这个方法拼接起来的statementId 
  // validateIncompleteStatements : 可以看到上面传进来的是true,这个方法还有其他地方调用传进来的是false,哪里调的自己找下;
  public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
    // 我们这里是true,就会进入buildAllStatements这个方法
    if (validateIncompleteStatements) {
      // 看名字就知道是构建用的,下面介绍干了点啥
      buildAllStatements();
    }
    // mappedStatements这个东西是map,判断了下缓存里面是否包含了statementName,一般情况下是都会包含的
    return mappedStatements.containsKey(statementName);
  }
  // 获取对象
  public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }

  // 返回map的value对象 其他的buildAllStatements上面介绍过了,过了
  public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
    if (validateIncompleteStatements) {
      buildAllStatements();
    }
    return mappedStatements.get(id);
  }
 
   /*
   * Parses all the unprocessed statement nodes in the cache. It is recommended
   * to call this method once all the mappers are added as it provides fail-fast
   * statement validation.
   * 这是源代码注释
   * 解析缓存中所有未处理的语句节点。
   * 建议在添加了所有映射器之后调用此方法,因为它提供了快速失败语句验证。
   * (有道翻译的我CV过来的,翻译错了你们去抽有道,别喷我)
   * 我自己的注释
   * 	这里就是判了几个东西是不是空,这几个东西都是LinkedList,初始化过了,这里的size都是0,所以进不去,我们直接过了(我想偷懒)
   */
  protected void buildAllStatements() {
    if (!incompleteResultMaps.isEmpty()) {
      synchronized (incompleteResultMaps) {
        // This always throws a BuilderException.
        incompleteResultMaps.iterator().next().resolve();
      }
    }
    if (!incompleteCacheRefs.isEmpty()) {
      synchronized (incompleteCacheRefs) {
        // This always throws a BuilderException.
        incompleteCacheRefs.iterator().next().resolveCacheRef();
      }
    }
    if (!incompleteStatements.isEmpty()) {
      synchronized (incompleteStatements) {
        // This always throws a BuilderException.
        incompleteStatements.iterator().next().parseStatementNode();
      }
    }
    if (!incompleteMethods.isEmpty()) {
      synchronized (incompleteMethods) {
        // This always throws a BuilderException.
        incompleteMethods.iterator().next().resolve();
      }
    }
  }
 

先陪女朋友过520,后面再补
520过完了,过完北京就封了,可以安心看源码了,接着继续吹(RNG牛逼)
接下来介绍MethodSignature对象,也是在MapperMethod类里面的,也是个public类,估计那会也想的能给其他地方也用
在这里插入图片描述
图贴完了继续贴代码

	/*
	* 入参有三个
	* configuration:还是配置类的那个
	* mapperInterface:mapper所在接口,我这里是【interface com.example.demo.Mapper.UserMapper】
	* method:方法,我这里是【public abstract com.example.demo.entity.User com.example.demo.Mapper.UserMapper.queryUser(int)】
	*/
    public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      // TypeParameterResolver我理解是个工具类
      // 获取返回类型的,在mybatis初始化的时候,会用到这个,解析sql的时候也会用到这个
      // 跟下源码,发现我们的返回resolvedReturnType 最终是【class com.example.demo.entity.User】这个实体对象
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      // 如果返回类型是class的话,就进这里了,其他的elseif没进去,后面补
      if (resolvedReturnType instanceof Class<?>) {
        //  给当前对象MethodSignature中的私有属性returnType 赋值
        this.returnType = (Class<?>) resolvedReturnType;
      } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
      } else {
        this.returnType = method.getReturnType();
      }
      // 底下都是给私有属性赋值,但是有几个方法啊,里面写了一堆(我yue你仙人,你写这么多shi呀),哦,mybatis从入门到放弃,nice,真不错
      // 判断了下返回类型是不是void,我们是【class com.example.demo.entity.User】,所以这里是个false
      this.returnsVoid = void.class.equals(this.returnType);
      // 判断返回类型是不是对象数组
      this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
      // 判断返回类型是不是Cursor类,这个类的作用是,流式查询 cursor 就是让查询的速度加快,自行百度怎么用
      this.returnsCursor = Cursor.class.equals(this.returnType);
      // 下面贴源码了,这里就是判断返回类型是不是map,获取mapkey的
      this.mapKey = getMapKey(method);
      // 返回类型是否为map,不是的话就是false,我们这里是【false】
      this.returnsMap = this.mapKey != null;
      // 如果方法参数中包含RowBounds类型或其子类型的参数,找出这个参数在方法参数列表中的下标值
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      // 如果方法参数中包含ResultHandler类型或其子类型的参数,找出这个参数在方法参数列表中的下标值
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      // 这里又new了个对象,yue你仙人,啊打啊打
      // 声明参数名称解析器,把参数名称丢map里面了,
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }

    // 返回类型是否为map,为map返回key
    private String getMapKey(Method method) {
      String mapKey = null;
      if (Map.class.isAssignableFrom(method.getReturnType())) {
        final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
        if (mapKeyAnnotation != null) {
          mapKey = mapKeyAnnotation.value();
        }
      }
      return mapKey;
    }

    public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }

	/*
	* 俩入参
	* method:你的请求SQL的方法和返回类型,我这里是【public abstract com.example.demo.entity.User com.example.demo.Mapper.UserMapper.queryUser(int)】
	* paramType:这里是写死的,mybatis这里调了两次,两次都不一样
	*   第一次是【class org.apache.ibatis.session.RowBounds】
	*   第二次是【interface org.apache.ibatis.session.ResultHandler】
	* 我们两次调用返回的都是null
	* 方法作用:如果方法参数中包含RowBounds类型或其子类型的参数,找出这个参数在方法参数列表中的下标值
	*/ 
    private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
      Integer index = null;
      final Class<?>[] argTypes = method.getParameterTypes();
      for (int i = 0; i < argTypes.length; i++) {
        if (paramType.isAssignableFrom(argTypes[i])) {
          if (index == null) {
            index = i;
          } else {
            throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
          }
        }
      }
      return index;
    }

--------------------------------------------华丽的分割线--------------------------------------------
  // 以下内容来自TypeParameterResolver类
  public static Type resolveFieldType(Field field, Type srcType) {
    // 获取方法的正确返回类型 我这里是【class com.example.demo.entity.User】
    Type fieldType = field.getGenericType();
    // 获取方法的class类对象,我这里是【interface com.example.demo.Mapper.UserMapper】
    Class<?> declaringClass = field.getDeclaringClass();
    // 这个就是返回最后的type了,里面还做了额外处理,就是一堆if else,我们直接进了else了,直接过了,底下贴了resolveType方法
    return resolveType(fieldType, srcType, declaringClass);

  // 这里就是获取type的,我们直接走的是最底下的else,其他几个ifelse不看了
  private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
    if (type instanceof TypeVariable) {
      return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
    } else if (type instanceof ParameterizedType) {
      return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
    } else if (type instanceof GenericArrayType) {
      return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
    } else {
      return type;
    }
  }
  }

    /*
    * 以下内容来自ParamNameResolver类
    * 俩入参,config不介绍了
    * method:外面透传进来的,我这里依旧是【public abstract com.example.demo.entity.User com.example.demo.Mapper.UserMapper.queryUser(int)】
    */ 
    public ParamNameResolver(Configuration config, Method method) {
    // 得到入参类型
    final Class<?>[] paramTypes = method.getParameterTypes();
    // 得到方法上面的注释
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    // 一天到晚new对象,new new new,new你仙人
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // 这里判断了下入参是不是RowBounds或ResultHandler,isSpecialParameter方法下面贴出来了
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      // 刊用了@Param注解了没
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        // 我们进入到了这里,这个属性干哈的我忘了
        if (config.isUseActualParamName()) {
          // 源码贴下面了,调了工具类的一个方法做了一些特殊处理,得到了入参的属性名字
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      // 上面new的那个map这里就用到了,往里面放索引和索引对应的属性名字的
      map.put(paramIndex, name);
    }
    // 这个是当中的一个私有类,最后把map丢ParamNameResolver类的names里面了
    // private final SortedMap<Integer, String> names;
    names = Collections.unmodifiableSortedMap(map);
  }

  // 俩入参,method是【public abstract com.example.demo.entity.User com.example.demo.Mapper.UserMapper.queryUser(int)】
  // paramIndex就是你入参下标
  private String getActualParamName(Method method, int paramIndex) {
    if (Jdk.parameterExists) {
      // 方法贴下面了,这里其实就是为了得到你的入参名字叫啥的,我这里是【id】
      return ParamNameUtil.getParamNames(method).get(paramIndex);
    }
    return null;
  }

  private static boolean isSpecialParameter(Class<?> clazz) {
    return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
  }

// 这个类里面的内容不多,我直接全贴出来了
@UsesJava8
public class ParamNameUtil {
  public static List<String> getParamNames(Method method) {
    return getParameterNames(method);
  }

  public static List<String> getParamNames(Constructor<?> constructor) {
    return getParameterNames(constructor);
  }
  // 可以看到都是调用的这里,主要是为了得到你的入参值名字
  private static List<String> getParameterNames(Executable executable) {
    final List<String> names = new ArrayList<String>();
    final Parameter[] params = executable.getParameters();
    for (Parameter param : params) {
      names.add(param.getName());
    }
    return names;
  }

  private ParamNameUtil() {
    super();
  }
}

上面将的都是构造并缓存MapperMethod 的,从这里开始要看执行了,这个方法也在MapperMethod类里面,上面new的俩对象,就是给这个方法准备的
20220608继续更新,产品给了一些CRUD的需求,搞得差不多了,继续淦mybatis源码,不能放弃,加油(希望来个狐朋狗友带我吃烧烤,或者打扰一下我,这样我就可以名正言顺的不写博客了,我当初为什么要写mybatis源码的博客呢?????)

  /*
  * 看方法就知道了,这就是个执行的方法,但是里面写了一坨又一坨
  * sqlSession:我这里是SqlSessionTemplate
  * args:你的入参,数组我这里入参是1
  */
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 判断你的SQL类型是CRUD哪一种,我是SELECT
    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:
        // 方法返回值为void && 方法参数中包含RowBounds类型或其子类型的参数
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
          // 多个返回值方法的查询
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
          // @MapKey注解的Map类型的返回值的查询的执行
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
          // Cursor游标类型的返回值的查询的执行
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          // 将参数转换为sql命令的参数,底下贴了源码
          Object param = method.convertArgsToSqlCommandParam(args);
          // 根据sqlSession执行selectOne方法获取结果,调用的是sqlSession的实现类,SqlSessionTemplate中的方法
          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;
  }

--------------------------------------------华丽的分割线--------------------------------------------
    // 这里写的是上面【将参数转换为sql命令的参数,底下贴了源码】注释的源码
    public Object convertArgsToSqlCommandParam(Object[] args) {
      // 这里我们看到又用到了ParamNameResolver类的方法,看看这个方法干啥用的
      return paramNameResolver.getNamedParams(args);
    }

	/*
	* 源码贴出来一行行瞅瞅
	* args是你的入参,我这里是[1]
	*/ 
	public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
      // 由于我的参数只有一个并且没有@Param注释,所以会进入到这里,直接返回上面村的names的第一个参数了,可以在本页搜下ParamNameResolver这个关键字,忘记的话,上面找到复习下
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

CURD的代码写的差不多了
20220701继续更新,接着继续看mybatis源码,继续接着吹

  /**
   * 这里是【SqlSessionTemplate】实现类
   * 这里有两个入参
   * statement:你执行的方法的完全路劲名,例如我这里是【com.example.demo.Mapper.UserMapper.queryUser】
   * parameter:查询的参数,例如我这里是【1】
   * SqlSessionTemplate实现类中的selectOne方法,调用了this.sqlSessionProxy,这个类我们看源码可以看到还是SqlSession接口,所以实际调用的是这个接口的实现类【DefaultSqlSession】中的方法
   */
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // 调用了【SqlSession】接口的实现类【DefaultSqlSession】中的selectOne,然后入参有两个,做了参数的透传
    return this.sqlSessionProxy.<T> selectOne(statement, parameter);
  }
  /*
  * 这里是【DefaultSqlSession】实现类了
  * 方法的两个入参上面解释过了
  */
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // 调用了内部的selectList方法
    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 <E> List<E> selectList(String statement, Object parameter) {
    // 调了内部的selectList重载方法,多了个RowBounds参数
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 从configuration中的map缓存数据中,拿到项目启动的时候对应的封装的sql对象
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 执行查询,executor有两个实现类,这里会调用【CachingExecutor】实现类中的query方法,我们可以看到入参还有个wrapCollection方法,对参数过了特殊处理,我们瞅瞅这个方法干了点啥
      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();
    }
  }

  // 可以看到这个方法就是做了一个ifelse判断,判断是否是集合
  private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("array", object);
      return map;
    }
    return object;
  }
  /*
  * 入参有四个
  * ms:sql执行对象
  * parameterObject:请求入参
  * rowBounds:
  * resultHandler:结果集执行器
  */
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 调用了对象中的getBoundSql方法,获取BoundSql
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 创建缓存key,底下贴了源码,看看他咋弄得
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 调了query方法,进行查询(还以为就没了,没想到query方法里还写了一堆,wc)
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  /*
  * MappedStatement类中的getBoundSql方法,入参就是一个参数
  * 
  */
  public BoundSql getBoundSql(Object parameterObject) {
  	// 调用了【RawSqlSource】实现类中的方法,RawSqlSource类中调用了【StaticSqlSource】的方法【getBoundSql】,在【StaticSqlSource】中其实就是new了个BoundSql对象
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    // 对象中的这个参数为空就从新new下对象
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      // 我看了下这里new的和上面那个没啥区别,不知道为啥又new一遍
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    // 调用的【SimpleExecutor】这个实现类的方法,但是我们点进去【SimpleExecutor】类,发现实际没有这个方法,因为【SimpleExecutor】继承了【BaseExecutor】抽象类,最终使用的是抽象类里面的方法
    return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
  }

  /*
  * 这个方法是BaseExecutor类中的
  */
  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    // 实例化了个对象
    CacheKey cacheKey = new CacheKey();
    // 把相关信息都调这个方法写的对象里面的updateList里面了
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          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);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

  /*
  * 这个方法是BaseExecutor类中的
  */
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 从对象里面拿缓存,缓存不为空做查询的处理
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    // 这里调用的是【SimpleExecutor】实现类的query方法,但是【SimpleExecutor】实现类里面并没有这个方法,因为SimpleExecutor继承了BaseExecutor,其实调用的是【BaseExecutor】的query方法
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

BaseExecutor中的query方法

/*
  * BaseExecutor类中的方法
  */
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  	// 往【ErrorContext】中的线程安全对象【ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>()】,放了个ErrorContext对象
    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<E> list;
    try {
      // 从缓存里面拿list
      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;
  }


  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    // 往本地缓存里面缓存了点东西
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 调用的子类的查询方法,这里调用的是【SimpleExecutor】这个类的doQuery方法
      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;
  }

https://blog.csdn.net/heroqiang/article/details/79078980
可以参考下这个连接,这里写的比较直接,没我这么啰嗦(有些代码注释我也是抄的这个链接里面的)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值