2.10、mybatis源码分析之sql执行过程以select为例

  • 在研究select执行过程之前先来介绍一个重要的类

一、SqlNode和SqlSource

  • 在myabtis初始化过程中可以知道,映射配置文件中的sql节点会被解析为MappedStatement对象,其中sql语句解析成SqlSource对象,sql语句中定义的Sql节点、文本节点等,则由SqlNode接口的响应实现。

1、SqlSource接口

SqlSource接口结构
public interface SqlSource {
//通过解析得到BoundSql对象,其中封装了包含?占位符号的sql语句以及绑定的实参
  BoundSql getBoundSql(Object parameterObject);
}

2、SqlNode

public interface SqlNode {
//根据用户传入的实际参数,解析该sqlNode所记录的动态的sql节点,当sql节点下的所有sqlnode
//完成解析后,我们就可以从dynamicContext中获取一条动态生成的完整的sql语句。
  boolean apply(DynamicContext context);
}
  • sqlNode有很多的实现类,比如ChooseSqlNode、ForEachSqlNode等

二、StatementHandler

  • 核心接口,完成mybatis中最核心的工作,接口功能很多,例如创建statement对象,为sql语句绑定实际参数,执行select、insert等多种类型的sql语句,并将结果集映射成结果对象。


    StatementHandler类结构图

1、RoutingStatementHandler

  • 很根据MapperdStatement中指定的statementType字段,创建相应的StatementHandler接口实现。

2、BaseStatementHandler

  • 实现了Statementhandler接口的抽象类,只提供了参数绑定的相关方法,并没有具体操作数据库的方法。依赖于ParameterHandler和ResultSetHandler组件
  • 实现了 StatementHandler接口中的prepare方法,该方法中调用instantiateStatement抽象方法初始化statement对象,然后配置相关的参数

3、PreparedStatementHandler等

  • 是BaseStatementHandler的实现类,是具体操作sql的地方

三、ResultHandler

  • StatementHandler接口在执行完指定的select语句之后,会将查询得到的结果集交给ResultSetHandler完成映射处理,ResultSetHandler除了负责映射select语句得到的结果集还会处理存储过程执行后的输出参数
public interface ResultSetHandler {
//处理结果集生成相应的结果对象集合
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
//处理结果集,返回相应的游标对象
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
//处理存储过程的输出对象
  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

1、DefaultResultSetHandler实现类

  • 重要的属性:
 private final Executor executor;
  private final Configuration configuration;
  private final MappedStatement mappedStatement;
  private final RowBounds rowBounds;
  private final ParameterHandler parameterHandler;
//用户指定用于处理结果的ResultHandler对象
  private final ResultHandler<?> resultHandler;
  private final BoundSql boundSql;
  private final TypeHandlerRegistry typeHandlerRegistry;
  private final ObjectFactory objectFactory;
  private final ReflectorFactory reflectorFactory;

四、select的执行流程

1、方法入口,执行代理接口的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //判断方法的父类是否是Object如果是就直接调用
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    }
    //获取MapperMethod并缓存
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
   private final Map<Method, MapperMethod> methodCache;
1、获取MapperMethod时候先判断methodCache有没有,没有的话那么直接创建然后放入methodCache中(下次调用直接从map中获?。?  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
    //构建mapperMethod方法
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
2、创建MapperMethod对象,SqlCommand和MethodSignature是MapperMethod的内部类
   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
     //SqlCommand包含了mapper.xml文件中的相关sql语句及属性信息
    this.command = new SqlCommand(config, mapperInterface, method);
    //MethodSignature包含了mapper接口方法签名的信息
    this.method = new MethodSignature(config, method);
  }
2.1、获取SqlCommand
   public SqlCommand(Configuration configuration, Class<?> declaringInterface, Method method) throws BindingException {
     //包名+方法名作为唯一的id
      name = declaringInterface.getName() + "." + method.getName();
      final MappedStatement ms;
      try {
        //根据id获取
        ms = configuration.getMappedStatement(name);
      } catch (Exception e) {
        throw new BindingException("Invalid bound statement (not found): " + name, e);
      }
      //获取crud类型如果是unknown就报错误
      type = ms.getSqlCommandType();
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
    }
2.2、获取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();
      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);
    }

2、执行MapperMethod的execute方法

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //根据SqlCommand类型判断是执行curd的哪一个
    //insert
    if (SqlCommandType.INSERT == command.getType()) {
        //将参数转换为sql命令的参数
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      //update
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      //delete
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      //select
    } else if (SqlCommandType.SELECT == command.getType()) {
    //MethodSignature方法签名的返回类型来进行不同的逻辑处理
      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 {
      //当不满足上面需求的时候进行这一步操作,先进行参数解析,包装为map返回
        Object param = method.convertArgsToSqlCommandParam(args);
        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;
  }  

3、DefaultSqlSession中执行sqlSession.selectOne方法

public <T> T selectOne(String statement, Object parameter) {
    // 调用selectList获取list列表,然后获取第一个。如果有多个那么就直接报错误
    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;
    }
  }

4、归根结底还是调用executor的query方法来执行

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        ////根据statementId获取MappedStatement,MappedStatement封装了增删改查的详细信息
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用executor的query
      List<E> result = executor.<E>query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
//包装参数为map类型
  private Object wrapCollection(final Object object) {
    if (object instanceof List) {
      StrictMap<Object> map = new StrictMap<Object>();
      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;
  }

5、执行cachingExecutor的query方法,

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取解析过动态标签的sql,BoundSql包含当前sql语句的详细信息和参数
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //创建缓存key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

6、获取BoundSql

public BoundSql getBoundSql(Object parameterObject) {
/*获取解析过动态标签的sql*/
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.size() <= 0) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // 检查参数映射中是否存在嵌套的resultMap并设置boolean标记
    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;
  }

7、执行BaseExecutor的query

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, key, parameterObject, boundSql);
        if (!dirty) {
          cache.getReadWriteLock().readLock().lock();
          try {
            @SuppressWarnings("unchecked")
            List<E> cachedList = (List<E>) cache.getObject(key);
            if (cachedList != null) return cachedList;
          } finally {
            cache.getReadWriteLock().readLock().unlock();
          }
        }
        List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        return list;
      }
    }
    return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

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());
    if (closed) throw new ExecutorException("Executor was closed.");
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      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();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    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 {
    //子类实现具体的查询逻辑
      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;
  }

8、这里在SimpleExecutor中真正的执行query(根据配置还可以在选择其他的Executor)

 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();
     //创建statementhandler,statementhandler是创建原生statement的处理器
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
     //准备Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

9、构建StatementHandler

 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   /*构造RoutingStatementHandler*/
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
   //拦截器执行
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
  //终于柳暗花明了
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根据MappedStatement中的statement类型来创建响应的statement处理器
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

10、statement的准备

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //获取数据库连接
    Connection connection = getConnection(statementLog);
    //进行响应的statement的转呗工作
    stmt = handler.prepare(connection);
    //给statement设置参数
    handler.parameterize(stmt);
    return stmt;
  }
public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
    //实例化对象的statement
      statement = instantiateStatement(connection);
      //设置超时时间
      setStatementTimeout(statement);
      //设置FetchSize
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
实例化过程以预处理statement为例:
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

11、设置参数过程

public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    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)) {
                    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);
                }
                //获取类型处理器
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                //获取jdbcType类型
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    /*为PreparedStatement设置值*/
                    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);
                }
            }
        }
    }
}

12、最后执行查询并进行参数封装

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //执行sql语句
    ps.execute();
    //参数封装
    return resultSetHandler.<E> handleResultSets(ps);
  }
select执行流程
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容