Mybatis源码分析(五)一张图告诉你SQL语句怎么执行

本文源代码来源于mybatis-spring-boot-starter的2.1.2版本

前面的章节陆续介绍了MapperPoxy的创建、MapperStatement的生成、Executor等核心组件,其实都是在为本文做铺垫。这篇我们详细介绍下Mybatis是怎么执行我们定义的sql语句的。篇幅较长,我先上张图。

时序图

Mybatis源码分析(五)一张图告诉你SQL语句怎么执行

环境准备

<!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.2</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
     <!--数据库连接池-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.26</version>
    </dependency>
复制代码

编写Dao和对应Mapper:

public interface RoleMapper {
    @Select("select * from role")
    @Results({
            @Result(property = "roleId",column = "role_id"),
            @Result(property = "roleName",column = "role_name")
    })
    List<Role> selectALl();
}
复制代码

编写单元测试,后面我们就用该测试方法就行MybatisSql语句执行的探究。

@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisApplicationTests {

    @Resource
    private RoleMapper roleMapper ;

    @Test
    public void query2(){
        List<Role> roles = roleMapper.selectALl();
    }
}
复制代码

MapperProxy

当我们把Mapper类交给Spring管理的时候,实际上Spring容器中存储的是Mapper的代理对象 MapperProxy 。使用代理对象调用我们自己的接口中的方法时,会执行 InvocationHandler 实现类的 invoke() 方法。无疑,MapperProxy的**invoke()**方法一定是Mybatis执行SQL的入口,一起来看下:

@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 {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
复制代码

cachedInvoker(method).invoke(proxy, method, args, sqlSession) 这里我们看到最终的查询结果实际上 cachedInvoker(method) 执行了invoke方法得到的。很明显 cachedInvoker(method) 也是一个代理对象,我们一起来看看它。

2.1 invoke

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
        //映射代理对象保存我们的查询方法缓存,如果第二次相同的方法,就会直接返回.
      return methodCache.computeIfAbsent(method, m -> {
        if (m.isDefault()) {
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }
复制代码

首先调用了 methodCache.computeIfAbsent 这个方法,这个是映射代理对象保存我们的查询方法缓存,我们首次调用肯定是没有缓存的,就会进入 new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())) 中。这里new了一个 MapperMethod 对象作为 PlainMethodInvoker 构造参数,这个对象我们一会儿介绍,继续看。 PlainMethodInvoker 是MapperProxy的一个 内部类

private static class PlainMethodInvoker implements MapperMethodInvoker {
   private final MapperMethod mapperMethod;

   public PlainMethodInvoker(MapperMethod mapperMethod) {
     super();
     this.mapperMethod = mapperMethod;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
     return mapperMethod.execute(sqlSession, args);
   }
 }
复制代码

PlainMethodInvoker 定义了成员属性 mapperMethod ,并重写了 invoke() 方法.所以绕了一圈最终执行的还是 MapperMethodexecute() 方法。我们来看看 MapperMethod 是何方神圣。

MapperMethod

我们先从MapperMethod构造说起:

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }
复制代码

MapperMethod有两个属性 commandmethod ,看起来一个是和sql有关,一个是和本身的方法有关。

3.1 SqlCommand

SqlCommand我们之前说过,它会根据Mapper接口名和方法名去 Configuration 中拿 MappedStatement

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
        //获取方法名
      final String methodName = method.getName();
      final Class<?> declaringClass = method.getDeclaringClass();
       //获取MappedStatement 接口名+方法名
      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 = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }
复制代码

3.2 MethodSignature

MethodSignature是对方法的出入参做一些处理,比如根据方法返回类型设置标记,入参参数名等,放在 method 中,后面在 ResultSetHandler 中会用到。

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.returnsOptional = Optional.class.equals(this.returnType);
      this.mapKey = getMapKey(method);
      //返回值是否是MAP
      this.returnsMap = this.mapKey != null;
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      //resultHandler类型参数的位置
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }
复制代码

3.3 execute

//这个方法是对SqlSession的包装调用
  public Object execute(SqlSession sqlSession, Object[] args) {
	//定义返回结果
    Object result;
	//如果是INSERT操作
    if (SqlCommandType.INSERT == command.getType()) {
	  //处理参数
      Object param = method.convertArgsToSqlCommandParam(args);
	  //调用sqlSession的insert方法
      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操作 那么情况会多一些 但是也都和sqlSession的查询方法一一对应
    } else if (SqlCommandType.SELECT == command.getType()) {
	   //如果返回void 并且参数有resultHandler
      //则调用 void select(String statement, Object parameter, ResultHandler handler);方法
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
	  //如果返回多行结果这调用 <E> List<E> selectList(String statement, Object parameter);
      //executeForMany这个方法调用的
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
	  //如果返回类型是MAP 则调用executeForMap方法
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else {
		//否则就是查询单个对象
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else if (SqlCommandType.FLUSH == command.getType()) {
        result = sqlSession.flushStatements();
    } else {
	  //如果全都不匹配 说明mapper中定义的方法不对
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
	//如果返回值为空 并且方法返回值类型是基础类型 并且不是VOID 则抛出异常
    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语句的类型分别处理,对比看起来都是先获取 param ,然后路由到 sqlSession 的某个方法。因为我们的selectAll()在执行 MethodSignature 方法时得到 returnsMany =true,所以我们跟进下

3.4 executeForMany

//如果参数含有rowBounds则调用分页的查询
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
    } else {
	//没有分页则调用普通查询
      result = sqlSession.<E>selectList(command.getName(), param);
    }
复制代码

这里会继续 sqlSession 的selectList()方法:

SqlSession

4.1 SqlSessionTemplate

MapperFactoryBean在创建MapperFactory在 getObject() 曾定义了SqlSession的类型—— SqlSessionTemplate

@Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  //==>
  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }
  
复制代码

我们来看下 SqlSessionTemplate 的selectList()方法

@Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.sqlSessionProxy.selectList(statement, parameter);
  }
复制代码

sqlSessionProxy 又是代理对象,老规矩继续看 invoke 方法:

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取SqlSqlSession对象,如果没有特殊绑定就调用sqlSessionFactory的openSession创建
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator
              .translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
复制代码

getSqlSession如果没有特殊配置会调用 sessionFactory.openSession(executorType) 进行创建:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //创建executor
      final Executor executor = configuration.newExecutor(tx, execType);
      //返回DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
复制代码
  • 返回的SqlSession实现类是 DefaultSqlSession
  • ExecutorTy默认是SIMPLE,这里会实例化一个 CachingExecutor 委托给 SimpleExecutor 执行具体的sql方法,前面的文章已经分析过了,这里不做介绍了。

4.2 DefaultSqlSession

我们继续来看DefaultSqlSession的 selectList() 方法:

@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //获取MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用executor的query方法
      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();
    }
  }
复制代码

Executor

5.1 CachingExecutor

上面提到了,在创建 SqlSession 的时候会实例化 CachingExecutor ,来看下 CachingExecutorquery 方法:

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) {
          // 委托给BaseExecutor的实现类继续执行
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //放入缓存
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    // 委托给BaseExecutor的实现类继续执行
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
复制代码

CachingExecutor 在执行的时候,会先判断是否有缓存,没有的话执行完写入缓存。 delegat 就是 CachingExecutor 的委托对象,默认是 SimpleExecutor

5.2 BaseExecutor

我们先来看下 SimpleExecutor 的父亲 BaseExecutor 的query:

@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());
    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) {
        //此处尝试对Callable类型的表达式进行处理,主要是针对mode=out类型的参数
        //存储过程使用
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //从数据库中获取并进行缓存处理,其也会调用子类需复写的doQuery()方法
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      //如果一级缓存的范围是statement级别,则每次查询都清空一级缓存
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
复制代码

这里有关于mybatis缓存的使用,我们后面会详细讲解。如果一级缓存是statement级别的,每次使用后都会清除。我们直接来看走数据库 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 {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
    //删除占位符
      localCache.removeObject(key);
    }
    //添加缓存
    localCache.putObject(key, list);
    //存储过程 out参数也加入缓存 这里可以忽略
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
复制代码

doQuery()是抽象方法,这里实际上是执行 SimpleExecutordoQuery() 方法。

5.3 SimpleExecutor

@Override
  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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //创建Statement对象,注意,这里就是JDBC协议的java.sql.Statement对象了
      stmt = prepareStatement(handler, ms.getStatementLog());
      //执行SQL语句
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
复制代码

doQuery主要三件事情:

  • 使用 configuration.newStatementHandler 创建StatementHandler ,我们在上一章节已经提到,这里不过多讲解。
  • 创建Statement(java.sql.Statement)对象。
  • 使用Statement对象执行sql语句。

创建Statement对象

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
复制代码

6.1 获取java.sql.Connection对象

protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }
复制代码

这个 transaction 是Srping的事务管理器 SpringManagedTransaction .

@Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }
复制代码

继续来看 openConnection() :

private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

    LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
        + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
  }
复制代码

dataSource就是我们配置在yml中的数据源,调用 org.springframework.jdbc.datasource.DataSourceUtilsdoGetConnection() 获取。

6.2 创建java.sql.Statement对象

Mybatis源码分析(五)一张图告诉你SQL语句怎么执行

调用链路是:SimpleExecutor->RoutingStatementHandler->BaseStatementHandler

@Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      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);
    }
  }
复制代码
  • 根据Connection对象来创建Statement对象,其默认实现类 PreparedStatementHandlerinstantiateStatement 方法。
  • 为Statement设置超时时间,statement的timeout不是整个查询的timeout,只是statement执行完成并拉取fetchSize数据返回的超时。
  • 为Statement设置FetchSize,对于查询数据量大的场景下,非常有必要设置 fetchSize ,否则全量拉取很容易OOM。

继续看 instantiateStatement() :

@Override
  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() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }
复制代码

connection.prepareStatement是创建Statement对象的核心,connection是druid的连接,由druid来实现。 DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder)

执行Sql

Mybatis源码分析(五)一张图告诉你SQL语句怎么执行

调用链路:SimpleExecutor->RoutingStatementHandler->PrepareStatementHandler

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //这里会调用druid的execute
    ps.execute();
    //执行结果
    return resultSetHandler.handleResultSets(ps);
  }
复制代码

7.1 execute

因为我们的数据源连接池配置的是druid。这里execute会调用到druid的实现:

@Override
    public boolean execute() throws SQLException {
        checkOpen();
        //增加sql执行次数
        incrementExecuteCount();
        //将sql保存在transactionInfo中
        transactionRecord(sql);

        // oracleSetRowPrefetch();

        conn.beforeExecute();
        try {
        //执行jdbc的execute
            return stmt.execute();
        } catch (Throwable t) {
            throw checkException(t);
        } finally {
            conn.afterExecute();
        }
    }
复制代码

7.2 handleResultSets

Mybatis源码分析(五)一张图告诉你SQL语句怎么执行

7.2.1 映射结果入口

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;
   //结果集的第一个结果
   ResultSetWrapper rsw = getFirstResultSet(stmt);

   List<ResultMap> resultMaps = mappedStatement.getResultMaps();
   int resultMapCount = resultMaps.size();
   validateResultMapsCount(rsw, resultMapCount);
   while (rsw != null && resultMapCount > resultSetCount) {
     ResultMap resultMap = resultMaps.get(resultSetCount);
     //根据resultMap处理rsw生成java对象
     handleResultSet(rsw, resultMap, multipleResults, null);
     //获取结果集的下一个结果
     rsw = getNextResultSet(stmt);
     cleanUpAfterHandlingResultSet();
     resultSetCount++;
   }

   String[] resultSets = mappedStatement.getResultSets();
   if (resultSets != null) {
   	//和resultMaps的遍历处理类似
     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++;
     }
   }

   return collapseSingleResultList(multipleResults);
 }
复制代码

resultMaphandleResultSets 方法的核心,当 mybatis 初始化完成后上面的配置都放到 MappedStatement.resultMaps 里面,在解析的时候就是通过 resultMap.id 取到对应的 resultMap 然后逐次解析。 来看下 handleResultSet(rsw, resultMap, null, parentMapping)

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        if (resultHandler == null) {
           // 创建默认的结果处理器
            DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
            // 处理结果集的行数据
            handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
            // 将结果加入multipleResults中       
            multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
复制代码

可以看到,虽然按照parentMapping和resultHandler分成了3种情况,但最终都进入了 handleRowValues 方法。来下看 handleRowValues 方法:

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    //嵌套结构
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
     //处理简单映射,本文先只分析简单映射
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }
复制代码

本文只分析简单映射,关于嵌套结构的处理后面有时间会单独再写一篇分析。继续看 handleRowValuesForSimpleResultMap

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
        // 根据 RowBounds 定位到指定行记录
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      //根据discriminate找到适合的ResultMap
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      //生成java对象,附带懒加载
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }
复制代码

7.2.2 创建实体对象

我们跟踪下 getRowValue() 中的 createResultObject 看下是怎么创建java对象的:

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    this.useConstructorMappings = false; // reset previous mapping result
    final List<Class<?>> constructorArgTypes = new ArrayList<>();
    final List<Object> constructorArgs = new ArrayList<>();
    //创建对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      for (ResultMapping propertyMapping : propertyMappings) {
        // issue gcode #109 && issue #149
       /* 如果开启了延迟加载,则为 resultObject生成代理类,
          如果仅仅是配置的关联查询,没有开启延迟加载,是不会创建代理类
        
             * 创建代理类,默认使用 Javassist 框架生成代理类。
             * 由于实体类通常不会实现接口,所以不能使用 JDK 动态代理 API 为实体类生成代理。
             * 并且将lazyLoader传进去了
        */
        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
          resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          break;
        }
      }
    }
    this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
    return resultObject;
  }
复制代码

继续来看 createResultObject

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
    throws SQLException {
  final Class<?> resultType = resultMap.getType();
  final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
  final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
  //存在适用的typeHanlder类,事实上一般为基本数据类型或者其封装类
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
  //有参构造函数的constructor映射
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
  //接口或者无参构造函数
    return objectFactory.create(resultType);
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {
  //有参构造函数的自动映射
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
复制代码

7.2.3 结果集映射

我们回到 getRowValue() 中,这里对是否自动映射分别做了处理,我们先来看处理自动映射的 applyAutomaticMappings ,他的判断条件是 shouldApplyAutomaticMappings :

private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
//resultMap配置了autoMapping = true
  if (resultMap.getAutoMapping() != null) {
    return resultMap.getAutoMapping();
  } else {
  //xml setting的属性autoMappingBehavior,有3个值:NONE(不启用),PARTIAL(不嵌套的时候启动),FULL(启动)
    if (isNested) {
      return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
    } else {
      return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
    }
  }
}
复制代码
  • applyAutomaticMappings :结果集中有的column,但resultMap中并没有配置。
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//创建自动映射的映射对
  List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  boolean foundValues = false;
  if (!autoMapping.isEmpty()) {
    for (UnMappedColumnAutoMapping mapping : autoMapping) {
     // 通过 TypeHandler 从结果集中获取指定列的数据
      final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
      if (value != null) {
        foundValues = true;
      }
      if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
        // gcode issue #377, call setter on nulls (value is not 'found')
      // 通过元信息对象设置 value 到实体类对象的指定字段上
        metaObject.setValue(mapping.property, value);
      }
    }
  }
  return foundValues;
}
复制代码
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  final String mapKey = resultMap.getId() + ":" + columnPrefix;
  //autoMappingsCache作为缓存,首先从缓存中获取
  List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
  //缓存未命中
  if (autoMapping == null) {
    autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
    final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName;
      if (columnPrefix != null && !columnPrefix.isEmpty()) {
        // When columnPrefix is specified,
        // ignore columns without the prefix.
        if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
          propertyName = columnName.substring(columnPrefix.length());
        } else {
          continue;
        }
      }
      //驼峰
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      //存在set方法
      if (property != null && metaObject.hasSetter(property)) {
      //resultMap的应映射中已存在,忽略
        if (resultMap.getMappedProperties().contains(property)) {
          continue;
        }
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
        } else {
        //没找到,根据autoMappingUnknownColumnBehavior属性(默认为NONE)进行处理:NONE(忽略),WARNING(log.warn),ERROR(抛异常)
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, property, propertyType);
        }
      } else {
      // //没找到,根据autoMappingUnknownColumnBehavior属性(默认为NONE)进行处理:NONE(忽略),WARNING(log.warn),ERROR(抛异常)
        configuration.getAutoMappingUnknownColumnBehavior()
            .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
      }
    }
    //加入缓存
    autoMappingsCache.put(mapKey, autoMapping);
  }
  return autoMapping;
}
复制代码
  • applyPropertyMappings :根据 <resultMap> 节点中配置的映射关系进行映射
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
  boolean foundValues = false;
  final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
  for (ResultMapping propertyMapping : propertyMappings) {
    String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    if (propertyMapping.getNestedResultMapId() != null) {
      // the user added a column attribute to a nested result map, ignore it
      //嵌套查询的属性,忽略column 
      column = null;
    }
    if (propertyMapping.isCompositeResult()
        || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
        || propertyMapping.getResultSet() != null) {
        //字段值
      Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
      // issue #541 make property optional
      final String property = propertyMapping.getProperty();
      if (property == null) {
        continue;
      } else if (value == DEFERED) {
        foundValues = true;
        continue;
      }
      if (value != null) {
        foundValues = true;
      }
      //赋值给对象
      if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(property, value);
      }
    }
  }
  return foundValues;
}
复制代码

ResultMap 获取映射对象 ResultMapping 集合。然后遍历 ResultMapping 集合,再此过程中调用 getPropertyMappingValue 获取指定指定列的数据,最后将获取到的数据设置到实体类对象中。

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    if (propertyMapping.getNestedQueryId() != null) {
        // 获取关联查询结果
        return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (propertyMapping.getResultSet() != null) {
        addPendingChildRelation(rs, metaResultObject, propertyMapping);
        return DEFERED;
    } else {
        final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
        final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
        // 从 ResultSet 中获取指定列的值
        return typeHandler.getResult(rs, column);
    }
}
复制代码

与handleResultSets相关的延时加载、关联查询、嵌套查询等知识点,由于篇幅有限,后面我们在开一篇文章介绍吧。

原文 

https://juejin.im/post/5eec8ad26fb9a0585f0e9ecc

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Mybatis源码分析(五)一张图告诉你SQL语句怎么执行

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址