Mybatis全方位剖析【三】——从一个查询看透Mybatis的工作原理

Mybatis全方位剖析【三】——Mybatis工作原理分析

一、Mybatis的工作流程

通过上一节我们了解到了MyBatis的基本使用,这一节来看一下MyBatis的体系结构以及工作原理,我们首先来再看一下MyBatis的工作流程

在这里插入图片描述
首先MyBatis启动的时候回去解析核心配置文件以及Mapper.xml映射文件,MyBatis会将我们配置文件中的所有信息都封装保存在全局的Configuration中,接下来我们操作数据库需要与数据库之间获得会话,即获得SqlSession对象,在获取SqlSession对象的时候MyBatis通过SqlSessionFactoryBuilder来构建出了工厂对象SqlSessionFactory对象,通过SqlSessionFactory会话工厂来获取SqlSession,获取与数据库之间的连接。在SqlSession中持有了Executor对象,用来对数据库进行操作,在Executor执行数据库操作的时候,会使用ParameterHandler来对参数进行操作,使用StatementHandler与数据库进行交互,使用ResultSetHandler来对获取的结果集进行处理。

二、Mybatis的工作原理

接下来以一个MyBatis编程式查询demo来进行分析:

    @Test
    public void testQueryById() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        try {
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            Blog blog = mapper.selectBlogById(1);
            System.out.println(blog);
        } finally {
            session.close();
        }
    }

1.SqlSessionFactory的构建
 
从上面demo可以看出大致分为四步,第一步通过SqlSessionFactoryBuilder构建SqlSessionFactory,来具体看看MyBatis在做了什么:
当我们在使用SqlSessionFactoryBuilder的build方法 构建 SqlSessionFactory 的时候,会对Mybatis的核心配置文件进行解析

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

在SqlSessionFactoryBuilder的build方法中会使用 XMLConfigBuilder 的 parse()方法对配置文件进行解析

public class SqlSessionFactoryBuilder {
	// ....
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
	//.....
}

在 parse()方法中,parseConfiguration()方法会选取configuration根标签开始解析

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

parseConfiguration()方法对核心配置文件中的一级标签进行解析

private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      /*
        properties标签:properties标签用于配置参数信息,比如最常见的数据库连接信息,在xml只需要${}引用就可以了
        properties属性:
          resource: resource 是相对路径
          url:      url指定本地服务器或者网络的绝对路径
       */
      propertiesElement(root.evalNode("properties"));
      /*
        settings标签:MyBatis的核心配置,其<settings>标签也解析成了一个 Properties 对象,对于<settings> 标签的子标签的处理在后面,
        先解析成Properties是因为后面两个方法需要使用
       */
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      /*
        读取本地文件或FTP文件的时候,需要用到VFS(Vitual File System)类
       */
      loadCustomVfs(settings);
      /*
        指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
       */
      loadCustomLogImpl(settings);
      /*
        typeAliases标签:
          TypeAliases是类型的别名,用来简化全路径类名的拼写,像我们的参数类型和返回值都可能会用到,在MyBatis中预定义的类型别名,在TypeAliasesRegistry中
       */
      typeAliasesElement(root.evalNode("typeAliases"));
      /*
      plugins标签:
          解析插件,植入插件逻辑,标签解析完之后会生成一个Interceptor对象,并且添加到Configuration的InterceptorChain 属性里面,它是一个 List
       */
      pluginElement(root.evalNode("plugins"));
      /*
        objectFactory标签:
          MyBatis在将数据库中的数据转换为java对象的时候,会通过ObjectFactory创建对象,我们可以自定义ObjectFactory,让MyBatis执行我们的创建对象逻辑
       */
      objectFactoryElement(root.evalNode("objectFactory"));
      /*
        用来实例化对象
       */
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      /*
        解析 reflectorFactory 标签,生成 ReflectorFactory 对象
       */
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      /*
        处理<settings>标签里面的所有子标签,子标签属性的默认值都是在这里赋值的,都会赋值到Configuration的相应属性里面去
       */
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      /*
        一个 environment 就是对应一个数据源,所以在这里我们会根据配 置的<transactionManager>创建一个事务工厂,
        根据<dataSource>标签创建一个数据源,最后把这两个对象设置成 Environment 对象的属性,放到 Configuration 里面
       */
      environmentsElement(root.evalNode("environments"));
      /*
        解析 databaseIdProvider 标签,生成 DatabaseIdProvider 对象(用来支持不同厂商的数据库
       */
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /*
        解析typeHandlers,用来做数据库和Java对象之间的相互转换,最后TypeHandler会注册到TypeHandlerRegistry
       */
      typeHandlerElement(root.evalNode("typeHandlers"));
      /*
        解析<mappers>标签,并解析Mapper.xml映射文件中的所有子标签,将每一个操作数据库的元素都解析为一个个MappedStatement对象,并且将 
        Mapper接口的Class<T>类和工厂类进行了绑定,同时会解析Mapper接口方法上的注解,最后同样会解析成一个个MappedStatement对象,注解
        和Mapper.xml配置文件作用相同,
       */
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

到这里所有配置文件,包括Mapper接口上的注解解析完成,全部设置到了Configuration对象里面

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

最后返回一个DefaultSqlSessionFactory对象,到这里第一步SqlSessionFactory的构建就完成了
 
2.SqlSession的构建
 
接下来看第二步SqlSession的构建,我们跟数据库的每一次链接,都需要创建一个会话,我们用openSession()方法来创建

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //在核心配置文件中transactionManager 配置的是JDBC则创建JdbcTransactionFactory,配置为MANAGED则创建ManagedTransactionFactory
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      /*
        读取<setting/>标签中defaultExecutorType配置的值,可配置为 SIMPLE、REUSE、BATCH,默认为SIMPLE, 创建 SimpleExecutor 执行器
        SIMPLE:普通的执行器                                    对应 SimpleExecutor
        REUSE:执行器会重用预处理语句(PreparedStatement)         对应 ReuseExecutor
        BATCH:执行器不仅重用语句还会执行批量更新                   对应 BatchExecutor
      */
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx);
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

如果在核心配置文件中配置了cacheEnabled=ture,会用装饰器模式对 executor 进行包装:new CachingExecutor(executor)

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //用CachingExecutor装饰Executor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //责任链模式注册所有插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

到这里SqlSession会话对象已经创建完成,最后获得了一个DefaultSqlSession,里面包含了一个 Executor,它是 SQL 的执行者。
 
3.获取Mapper对象
 
现在我们已经得到了 DefaultSqlSession,并且也有了Sql执行器Executor,所有我们必须要能够找到Mapper.xml里面定义的Statement Id 才能执行对应的Sql语句,在MyBatis中,找到 Statement ID 有两种方式:一种是直接调用 session 的方法,在参数里面传入 Statement ID,这种方式属于硬编码,我们没办法知道有多少处调用,修改起来也很麻烦,另一种是定义一个接口,然后再调用 Mapper 接口的方法。由于我们的接口名称跟 Mapper.xml 的 namespace 是对应的,接口的方法跟 statement ID 也都是对应的,所以根据方法就能找到对应的要执行的 Sql。接下来我们来具体看一下是怎么获取Mapper对象的:在上面demo代码中调用了session.getMapper(Class<T> type)方法

	BlogMapper mapper = session.getMapper(BlogMapper.class);
public class DefaultSqlSession implements SqlSession {
  //.....
  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }
  //.....
}
public class Configuration {
  //.....
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  //.....
}

传入Class接口的Class对象,获取到MapperProxyFactory工厂对象,调用了它的newInstance()方法

public class MapperRegistry {
  //....
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //这里knownMappers是在初始化Configuration对象的时候解析注册进去的
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  //....
}

这里newInstance方法中使用了JDK的代理模式,这里的MapperProxy就是实现了InvocationHandler的触发管理类

public class MapperProxyFactory<T> {
  protected T newInstance(MapperProxy<T> mapperProxy) {
   /**
     * @Description: 使用JDK动态代理创建Mapper代理对象
     * @param ClassLoader
     * @param interfaces
     * @param InvocationHandler 实现了InvocationHandler接口的触发管理类
     * @return Mapper代理对象
     * @Author zdp
     * @Date 2022-01-04 11:18
     */
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    //创建MapperInterface对应的MapperProxy 对象,比如mapperInterface是 BlogMapper.class,就创建MapperProxy
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

到这里也解释了为什么接口要绑定一个MapperProxyFactory工厂对象,这个工厂类的作用就是创建Mapper代理对象的,但是在MyBatis中的代理和平时的JDK代理有点不同,JDK的动态代理中,在实现了 InvocationHandler 的代理类里面,需要传入一个被代理对象的实现类,而MyBatis中并不需要实现类,因为MyBatis并不是真正要增强被代理对象,而是只需要根据接口类型+方法的名称,就可以找到 Statement Id,就可以执行Sql了。到这里已经得到了一个Mapper的代理对象,接下来看看Sql是怎么执行的
 
4.执行Sql
 
因为所有的Mapper对象都是通过MapperProxy代理出来的,所以当我们在执行Mapper方法的时候,都会去执行到Mapper的invoke()方法

public class MapperProxy<T> implements InvocationHandler, Serializable {
  //.....
  @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);
      } else {
        //像上面调用demo中的 selectBlogById 方法都会在这里开始执行
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
 //.....
}

这里先从缓存methodCache<K,V>中去根据 method (method就是缓存中的Key) 获取,如果没有获取到MapperMethodInvoker,则执行 m -> {}这个自定义的实现,返回自定义的MapperMethodInvoker

  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, 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 {
          //这里new MapperMethod()的时候会去初始化SqlCommand,会根据传入的method拿到执行方法的名称,通过mapperInterface拿到接口名称,通过手动拼接的方式拿到StatementId,然后根据StatementId去Configuration中获取Ms对象,
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }
public class MapperMethod {
  /**
   * @Description: MapperMethod构造方法
   * @Author zdp
   * @Date 2022-01-07 14:25
   */
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }
}
public static class SqlCommand {
	    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      final String methodName = method.getName();
      final Class<?> declaringClass = method.getDeclaringClass();
      //获取ms对象
      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就是获取的StatementId = namespace + methodName
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }
}

看下根据StatementId 获取ms对象的过程

    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
        Class<?> declaringClass, Configuration configuration) {
      //根据接口名 + 方法名 拼接得到 StatementId
      String statementId = mapperInterface.getName() + "." + methodName;
      if (configuration.hasStatement(statementId)) {
        //从configuration获取ms对象,之前解析Mapper.xml文件的时候说了,Mapper.xml里面的像select、update等元素都会被解析为一个个ms对象保存在configuration中
        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;
    }

接着看invoke()方法这里我们会走到 PlainMethodInvoker调用其invoke方法

  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);
    }
  }

接下来调用了execute()方法,这里我们以查询为例就只看查询的逻辑了,

public class MapperMethod {
  public Object execute(SqlSession sqlSession, Object[] args) {
    //....
    switch (command.getType()) {
      case SELECT:
        //判断是否没有返回值, void.class.equals(this.returnType);
        //
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        //判断方法返回值是否是集合或者数组
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        //判断返回值是否是Map
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        //判断返回值是否是Cursor     Cursor.class.equals(this.returnType)
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          //转换为sql执行参数
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
       //....
    }
  }
}

我们返回值是Blog对象,这里会走到else里面,可以看到执行了selectOne()方法,

public class DefaultSqlSession implements SqlSession {
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.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;
    }
  }

  //
  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

最终selectOne方法还是调用的selectList方法,到这里我们是不是可以看到一段熟悉的异常,当查询返回一个对象的时候,却查询到了多条数据,就会抛出TooManyResultsException异常,我们接着看selectList()方法最后是使用executor调用了query()方法 ,这里我们没有去关闭二级缓存,那么其实会使用CachingExecutor 调用其 query()方法,若是关闭了二级缓存则会走到BaseExecutor 的 query()方法

public class CachingExecutor implements Executor {
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取执行Sql
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //创建缓存Key,后续若是再次查询,则通过该值获取结果
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  
  @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.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}

上面通过ms获取了一个缓存对象,这里我们没有在Mapper.xml映射文件中配置<cache/>缓存标签(不清楚MyBatis缓存的可以看这),这里的cache为空,直接走下面的query()方法

public abstract class BaseExecutor implements Executor {
	@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
	//....
    //ms.isFlushCacheRequired() 判断的是 Select 元素的flushCache值是否为true, select默认为false
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      //清空缓存
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      //通过缓存Key先从缓存获取
      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--;
    }
	//....
    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;
  }

doQuery()看看MyBatis从数据库查询数据的过程,在这里创建了StatementHandler,这里StatementHandler实例的创建取决于在Mapper.xml映射文件中Select元素配置的statementType类型,默认是PREPARED,所以这里创建的handler实例是 PreparedStatementHandler

public class SimpleExecutor extends BaseExecutor {
  @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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
}

创建StatementHandler 的同时也创建了处理参数的ParameterHandler 和处理结果集的ResultSetHandler

public abstract class BaseStatementHandler implements StatementHandler {
  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //......
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }
}

完成StatementHandler 的创建之后,调用prepareStatement(handler, ms.getStatementLog());方法对语句进行预编译,处理参数,最后调用PreparedStatementHandler的query()方法

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //这里的execute就是JDBC包中的方法了
    ps.execute();
    //调用DefaultResultSetHandler进行结果集处理
    return resultSetHandler.handleResultSets(ps);
  }

到这里一个简单的查询流程就完成了,最后用一句话简单总结一下整个流程:
首先MyBatis会去加载核心配置文件,Mapper.xml映射文件,将所有的配置信息都会封装到全局的Configuration对象中,为了与数据库进行交互需要SqlSession,在此之前通过SqlSessionFactoryBuilder构建出会话工厂SqlSessionFactory会话工厂,通过会话工厂拿到SqlSession对象,同时也创建出了Executor执行器,之后通过Mapper接口绑定的MapperProxyFactory工厂对象获得代理的Mapper对象,然后通过Mapper的namespace+method作为StatementId,从Configuration对象中拿到MappedStatement 对象,从MappedStatement 对象拿到执行Sql,之后通过ParameterHandler 处理Sql参数,通过StatementHandler执行Sql语句,最后ResultSetHandler处理结果集。

以上就是MyBatis工作原理的简单介绍了,如有错误希望你指正,希望能对你有点帮助!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值