【Mybatis-源码解析】2. SQL 执行流程

475 阅读7分钟

2. SQL 执行流程

SELECT

graph LR

 mapper-interface --> MapperProxy.invoke --> 根据类型调用sqlSession的查询方法
 
 根据类型调用sqlSession的查询方法--> 返回的结果类型为空并且存在结果处理器 --> 调用SqlSessionTemplate.select方法 --> 调用默认实现的DefaultSqlSession的select方法 --> 调用executor.query --> 调用StatementHandler.query方法
 
 根据类型调用sqlSession的查询方法--> 返回的结果类型为集合 --> 调用SqlSessionTemplate.selectList方法 --> 调用默认实现的DefaultSqlSession的selectList方法 --> 调用executor.query --> 调用StatementHandler.query方法
  
 根据类型调用sqlSession的查询方法--> 返回的结果类型为MAP --> 调用SqlSessionTemplate.selectMap方法 --> 调用默认实现的DefaultSqlSession的selectMap方法 --> 调用executor.query --> 调用StatementHandler.query方法
   
 根据类型调用sqlSession的查询方法--> 返回的结果类型为坐标 --> 调用SqlSessionTemplate.selectCursor方法 --> 调用默认实现的DefaultSqlSession的selectCursor方法 --> 调用executor.queryCursor --> 调用StatementHandler.query方法
   
 根据类型调用sqlSession的查询方法--> 返回的结果类型为非以上场景 --> 调用SqlSessionTemplate.selectOne方法 --> 调用默认实现的DefaultSqlSession的selectOne方法 --> 调用executor.query --> 调用StatementHandler.query方法
 
 调用StatementHandler.query方法 --> 添加本地缓存 --> 根据返回类型处理结果
 
  • 判断查询的结果
    • 如果查询结果返回为void 并且有结果处理器,那么 处理完 直接返回null (使用:正常配置resultMap,在参数中设置ResultHandler)

      1. 处理参数
      2. 判断是否分页,如果分页执行 sqlSession.select(x,x,rowBounds,ResultHandler)方法
      3. 如果不分页执行 sqlSession.select(x,x,ResultHandler)方法
        1. 调用CachingExecutor的query方法 (如果缓存中有直接获取缓存对象,如果缓存中没有调用BaseExecutor的query方法(见2.1 Executor)
        2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doQuery方法
        3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.query方法(见2.2 StatementHandler),构建结果的时候检查结果是否有结果处理器,如果有就执行
        4. return null;
    • 如果查询结果的是集合 那么执行 executeForMany 方法 (使用:返回值为List或者Collection的实现类)

      1. 处理参数
      2. 判断是否分页,如果分页执行 sqlSession.selectList(x,x,rowBounds)方法
      3. 如果不分页执行 sqlSession.selectList(x,x)方法
        1. 调用CachingExecutor的query方法 (如果缓存中有直接获取缓存对象,如果缓存中没有调用BaseExecutor的query方法(见2.1 Executor)
        2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doQuery方法
        3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.query方法(见2.2 StatementHandler)
      4. 判断类型是否匹配,如果类型不匹配判断是不是数组,如果是数组进行转换为数组,如果不是 数组转换为结果
    • 如果查询结果的Map 那么执行 executeForMap 方法 (使用:resultType="java.util.HashMap",方法添加@MapKey注解)

      1. 处理参数
      2. 判断是否分页,如果分页执行 sqlSession.selectMap(x,x,mapKey,rowBounds)方法
      3. 如果不分页执行 sqlSession.selectMap(x,x,mapKey)方法
        1. 调用CachingExecutor的query方法 (如果缓存中有直接获取缓存对象,如果缓存中没有调用BaseExecutor的query方法(见2.1 Executor)
        2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doQuery方法,
        3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.query方法(见2.2 StatementHandler)
        4. 创建一个MAP结果执行器
        5. 循环结果 将最终结果根据mapKey转换为MAP
    • 如果查询结果的Cursor 那么执行 executeForCursor 方法

      1. 处理参数
      2. 执行 sqlSession.selectOne(x,x)方法
        1. 调用sqlSession.selectList(x,x)方法

          1. 调用CachingExecutor的query方法 (如果缓存中有直接获取缓存对象,如果缓存中没有调用BaseExecutor的query方法(见2.1 Executor)
          2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doQuery方法,
          3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.queryCursor方法(见2.2 StatementHandler)
        2. 获取的结果判断结果大小,如果超过1个抛出异常

        3. 获取List的第一个结果

      Cursor 获取对象最终调用handleRowValues方法(实例化通过constructor.newInstance(),属性通过包装类的方法缓存(MAP)将值通过反射调用SET方法写入对象中)

    • 否则 处理参数 后 执行 selectOne方法

      1. 处理参数
      2. 判断是否分页,如果分页执行 sqlSession.selectCursor(x,x,rowBounds)方法
      3. 如果不分页执行 sqlSession.selectCursor(x,x)方法
        1. 调用CachingExecutor的query方法 (如果缓存中有直接获取缓存对象,如果缓存中没有调用BaseExecutor的query方法(见2.2 Executor)
        2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doQuery方法,
        3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.queryCursor方法(见2.3 StatementHandler)
        4. 返回Cursor 对象

UPDATE

graph LR
 
 mapper-interface --> MapperProxy.invoke --> 根据类型调用sqlSession的修改方法 --> 返回的结果类型为空并且存在结果处理器 --> 调用SqlSessionTemplate.update方法 --> 调用默认实现的DefaultSqlSession的update方法 --> 调用executor.update --> 调用StatementHandler.update方法
 
  • 将参数转换为Sql命令参数
  • 调用sqlSession.update(command.getName(), param)方法执行 (第一个参数为映射的方法名)
    1. 调用executor的update方法(见2.2 Executor)
    2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doUpdate方法,
    3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.update方法(见2.2 StatementHandler)
    4. 最终将sql做成数据包发送给服务器

INSTER

graph LR
 
 mapper-interface --> MapperProxy.invoke --> 根据类型调用sqlSession的insert方法 --> 返回的结果类型为空并且存在结果处理器 --> 调用SqlSessionTemplate.insert方法 --> 调用默认实现的DefaultSqlSession的update方法 --> 调用executor.update --> 调用StatementHandler.update方法
 
  • 将参数转换为Sql命令参数
  • 调用sqlSession.insert(command.getName(), param)方法执行 (第一个参数为映射的方法名)
  • sqlSession调用自己的update方法
    1. 调用executor的update方法(见2.2 Executor)
    2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doUpdate方法,
    3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.update方法(见2.2 StatementHandler)
    4. 最终将sql做成数据包发送给服务器

DELETE

graph LR
 
 mapper-interface --> MapperProxy.invoke --> 根据类型调用sqlSession的delete方法 --> 返回的结果类型为空并且存在结果处理器 --> 调用SqlSessionTemplate.delete方法 --> 调用默认实现的DefaultSqlSession的update方法 --> 调用executor.update --> 调用StatementHandler.update方法
 
  • 将参数转换为Sql命令参数
  • 调用sqlSession.delete(command.getName(), param)方法执行 (第一个参数为映射的方法名)
  • sqlSession调用自己的update方法
    1. 调用executor的update方法(见2.2 Executor)
    2. 根据Configuration的ExecutorType执行器类型最终调用子方法的doUpdate方法,
    3. 再根据Configuration这个方法的MappedStatement构建Statement,Statement.update方法(见2.2 StatementHandler)
    4. 最终将sql做成数据包发送给服务器

入口类源码解析

1. 调用MapperProxy代理类

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 判断是否是Object的方法,如果是直接执行代理类方法
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            // 判断是不是默认方法(是公共的接口方法)
            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        // 在缓存列表中获取到代理方法 (如果没有缓存进行加载)
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        // 执行方法 (入参为sql上下文)
        return mapperMethod.execute(this.sqlSession, args);
    }

缓存方法

    private MapperMethod cachedMapperMethod(Method method) {
        // 判断缓存中是否存在该方法 如果没有 创建映射方法对象
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
          mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
          methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
    
    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config){
        // 构建 SQL 命令
        this.command = new SqlCommand(config, mapperInterface, method);
        // 构建 MethodSignature (方法签名)
        this.method = new MethodSignature(config, mapperInterface, method);
    }
构建 SQL 命令
    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      // 获取 方法名称
      final String methodName = method.getName();
      // 获取 方法的声明类
      final Class<?> declaringClass = method.getDeclaringClass();
      // 解析SQL语句
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
          configuration);
      // 如果 映射陈述(MappedStatement)为空
      if (ms == null) {
        // 判断方法是否有 flush 注解 如果有 执行flush方法
        if (method.getAnnotation(Flush.class) != null) {
          name = null;
          type = SqlCommandType.FLUSH;
        } else {
        // 否则 抛出 没有映射的异常
          throw new BindingException("Invalid bound statement (not found): "+ mapperInterface.getName() + "." + methodName);
        }
      } 
      // 如果映射不为空
      else {
        // 获取到映射ID(方法名) 和 SQL 类型
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }
    
    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
      // 构建 陈述(缓存KEY)ID (方法全限定名)
      String statementId = mapperInterface.getName() + "." + methodName;
      // 判断配置中是否存在 缓存KEY (在启动的时候写入在 configuration.mappedStatements 中)
      if (configuration.hasStatement(statementId)) {
      // 如果有直接返回 如果没有返回 null
        return configuration.getMappedStatement(statementId);
      } 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;
    }
构建 MethodSignature (方法签名)
    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();
      // 判断是否返回 光标(Cursor)
      this.returnsCursor = Cursor.class.equals(this.returnType);
      // 判断返回值是否为MAP(获取MAP 的 key)
      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);
    }

执行方法

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        // 获取sql类型 (select update delete 还是 insert)
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            // 如果返回结果为void 并且 有结果处理程序 处理结构 之后 返回null
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            // 如果返回的是集合类 执行:executeForMany
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            // 如果返回的是Map 执行:executeForMap
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            // 如果返回的是Cursor 执行:returnsCursor
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                // 将参数转换为Sql命令参数
                param = this.method.convertArgsToSqlCommandParam(args);
                // 最终执行sql上下文的方法
                result = sqlSession.selectOne(this.command.getName(), param);
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

文章链接