mybatis 源码分析(二) sql执行路径分析

# mybatis 源码分析(二) sql执行路径分析


上一章我们分析了mybatis的初始化过程,今天我们来了解下

mybatis sql的执行原理

MapperProxy

上一章也分析到mybaits生成的mapper bean 实际上是由MapperProxy 这个类生成的动态代理类.所以今天从这个类下手

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  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;
  }
  /** 代理mapper接口中的所有方法 */
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    /** 执行方法 */
    return mapperMethod.execute(sqlSession, args);
  }
  /** 缓存mapperMethod */
  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;
  }
}

MapperMethod

public class MapperMethod {
   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    /** 方法对应的sql信息 */
    this.command = new SqlCommand(config, mapperInterface, method);
    /** 方法的签名 */
    this.method = new MethodSignature(config, method);
  }
  /** 所有的sql执行方法都是从SqlSession为入口的 
   *  从第一章知道 SqlSession的默认实例对象是 DefaultSqlSession 
   */
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        /** command.getName 返回的是 类的全限定名+方法 */
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      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;
  }
}

DefaultSqlSession

public class DefaultSqlSession implements SqlSession
{
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
    /** 这里遇到了一个很重要的类 MappedStatement 这个类包含了SQL执行的参数
     *  也包括了 SQL 构建类 SqlSource,
     *  该类有个参数 id 是以 类的全限定名+方法 构建的.
     *  ex: com.sun.mapper.EntityMapper.selectEntityById
     *  mybatis 生成ms的地方 解析mapper xml中的sql语句的时候(上一章有提到)
     *  还有就是 使用了注解的接口 比如 使用了
     *  tk.mybatis.mapper.common.Mapper
     *  我们来看下这2个地方的实现
     */
    MappedStatement ms = this.configuration.getMappedStatement(statement);
    this.executor.query(ms, wrapCollection(parameter), rowBounds, handler);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
                }
        }
}

mapper Xml 解析 添加MS

public class XMLMapperBuilder extends BaseBuilder {
 public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      /** 配置XML元素 */
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      /** 绑定注解类mapper */
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

    private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
          throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      /** 构建MS 继续跟踪下去会到MapperBuilderAssistant的 addMappedStatement 方法*/
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }
 }

MapperBuilderAssistant

public class MapperBuilderAssistant extends BaseBuilder {
      public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) throw new IncompleteElementException("Cache-ref not yet resolved");

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
    statementBuilder.resource(resource);
    statementBuilder.fetchSize(fetchSize);
    statementBuilder.statementType(statementType);
    statementBuilder.keyGenerator(keyGenerator);
    statementBuilder.keyProperty(keyProperty);
    statementBuilder.keyColumn(keyColumn);
    statementBuilder.databaseId(databaseId);
    statementBuilder.lang(lang);
    statementBuilder.resultOrdered(resultOrdered);
    statementBuilder.resulSets(resultSets);
    setStatementTimeout(timeout, statementBuilder);

    setStatementParameterMap(parameterMap, parameterType, statementBuilder);
    setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
    setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);

    MappedStatement statement = statementBuilder.build();
    /** 最终通过mybatis的配置类完成了添加操作 */
    configuration.addMappedStatement(statement);
    return statement;
  }
}

注解方法 添加MS

public class XMLMapperBuilder extends BaseBuilder {
      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#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          /** 从这里跟踪下去 会到MapperRegistry的addMapper 方法 */
          configuration.addMapper(boundType);
        }
      }
    }
  }
}

MapperRegistry

public class 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 {
        knownMappers.put(type, new MapperProxyFactory<T>(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 看名字就知道是通过注解来构建mapper的 */
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        /** 继续往下跟 */
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
}

MapperAnnotationBuilder

public class MapperAnnotationBuilder {

 public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
    String resource = type.getName().replace('.', '/') + ".java (best guess)";
    this.assistant = new MapperBuilderAssistant(configuration, resource);
    this.configuration = configuration;
    this.type = type;
    /** 构造函数中添加需要解析的注解 */
    sqlAnnotationTypes.add(Select.class);
    sqlAnnotationTypes.add(Insert.class);
    sqlAnnotationTypes.add(Update.class);
    sqlAnnotationTypes.add(Delete.class);

    sqlProviderAnnotationTypes.add(SelectProvider.class);
    sqlProviderAnnotationTypes.add(InsertProvider.class);
    sqlProviderAnnotationTypes.add(UpdateProvider.class);
    sqlProviderAnnotationTypes.add(DeleteProvider.class);
  }

public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) {
          /** 判断是否是 桥接方法 */
          if (!method.isBridge()) { // issue #237
            /** 生成ms */
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }
    void parseStatement(Method method) {
      /** 方法太长了 省略 重点 来了 
       * assistant是MapperBuilderAssistant的实例的引用 
       * 所以这里跟xml的解析最终走到一起了
       */
      assistant.addMappedStatement(
          mappedStatementId,
          sqlSource,
          statementType,
          sqlCommandType,
          fetchSize,
          timeout,
          null,                             // ParameterMapID
          parameterTypeClass,
          resultMapId,    // ResultMapID
          getReturnType(method),
          resultSetType,
          flushCache,
          useCache,
          false, // TODO issue #577
          keyGenerator,
          keyProperty,
          keyColumn,
          null,
          languageDriver,
          null);
    }
  }
}

现在 我们都知道了MappedStatement 的来源了 以及作用了。接下来就从头看DefaultSqlSession.select 方法

DefaultSqlSession

public class DefaultSqlSession implements SqlSession {
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      /** statement 入参就是 类的全限定名字 + 方法名 */
      MappedStatement ms = configuration.getMappedStatement(statement);
      /** 在第一章说到 executor 默认实现是 SimpleExecutor */
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
 }

SimpleExecutor

public class SimpleExecutor extends BaseExecutor {
    /** 该方法在父类申明的 */
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    /** 获取sql 注解方法就是由DynamicSqlSource 动态生成的
    *   其他方法就是从xml中读到的sql
    */
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

   public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      /** prepareStatement组装一个Statement */
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    /** 动态获取一个连接 */
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }
}

之后就是拿到connection 执行sql ~~~~ over

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值