<返回更多

Mybatis原理

2022-04-11    炯哈哈
加入收藏

MyBatis原理

mybatis源码原理主要是分为三个部分 初始化、创建会话、语句执行

一、初始化

1.读取配置文件mybatis-config.xml。
2.解析mApper.xml文件。
3.最后将mapper.xml中的sql语句全部保存到了Map mappedStatements中(mappedStatements的key:namespace+sqlId,value:MappedStatement
mappedStatements还会保存namespace和代理工厂的映射关系,存入到knownMappers.put(type, newMapperProxyFactory<>(type));)。

源码实现

public void prepare() throws IOException {
    //加载mybatis-config.xml配置文件
    String resource = "mybatis-config.xml";
    //将mybatis配置文件转换成流的形式
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //new一个SqlSessionFactoryBuilder,然后将流传进去,是为了得到一个 sqlSessionFactory 
    //这里使用到了建造者模式
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}

我们通过上面的build开始阅读我们mybatis的源码

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 开始解析 mybatis-config.xml,并且创建了 Configuration 对象 
    //Configuration 对象是对应mybatis-config.xml中的一些配置的
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 解析XML,最终返回一个 DefaultSqlSessionFactory >>
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

解析mybatis-config.xml 从 configuration 开始解析,也就是我们xml中的最上层配置开始解析

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

是一层一层对mybatis-config.xml进行解析,并将所有的标签都放到了Configuration中,我们可以看到一下代码中的参数

private void parseConfiguration(XNode root) {
  try {
    // 对于全局配置文件各种标签的解析,将标签放到Configuration中
    propertiesElement(root.evalNode("properties"));
    // 解析 settings 标签
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);
    // 类型别名
    typeAliasesElement(root.evalNode("typeAliases"));
    // 插件
    pluginElement(root.evalNode("plugins"));
    // 用于创建对象
    objectFactoryElement(root.evalNode("objectFactory"));
    // 用于对对象进行加工
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 反射工具箱
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    // settings 子标签赋值,默认值就是在这里提供的 >>
    settingsElement(settings);
    
    // 创建数据源
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析引用的Mapper映射器 这个是最重要的操作 
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

我们来看一下最重要的方法都做了哪些操作

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // 不同的定义方式的扫描,最终都是调用 addMapper()方法(添加到 MapperRegistry)。这个方法和 getMapper() 对应
      // <package name="com.xinyu.demo"/>  包含这样的标签才会走if操作
      //else 走的是 <mapper resource="BlogMapper.xml"/>这样的标签
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          // resource  相对路径
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // 解析 Mapper.xml,详情看下方代码块
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          // url 绝对路径
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          // class   单个接口
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

把sql都封装成一个个的statement,然后我们再去把namespace+id绑定一个代理的工厂,后面用来调用,最终也是调用的MapperRegistry.addMapper

public void parse() {
  // 总体上做了两件事情,对于语句的注册和接口的注册
  if (!configuration.isResourceLoaded(resource)) {
    // 1、具体增删改查标签的解析。
    // 一个标签一个MappedStatement。 
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    // 2、把namespace(接口类型)和工厂类绑定起来,放到一个map。
    // 一个namespace 一个 MapperProxyFactory
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

parse方法中的第一步configurationElement()操作参考以下代码

private void configurationElement(XNode context) {
  try {
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    // 添加缓存对象
    cacheRefElement(context.evalNode("cache-ref"));
    // 解析 cache 属性,添加缓存对象
    cacheElement(context.evalNode("cache"));
    // 创建 ParameterMapping 对象
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // 创建 List<ResultMapping>
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // 解析可以复用的SQL
    sqlElement(context.evalNodes("/mapper/sql"));
    // 解析增删改查标签,得到 MappedStatement 也是重要的
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

private void buildStatementFromContext(List<XNode> list) {
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  // 解析 Statement list代表的是XXXMapper.xml中的所有的sql语句
  buildStatementFromContext(list, null);
}


private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    // 用来解析增删改查标签的 XMLStatementBuilder
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      // 解析 Statement,添加 MappedStatement对象 详情看下方代码
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

 
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);

  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }

  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String resultType = context.getStringAttribute("resultType");
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  if (resultSetTypeEnum == null) {
    resultSetTypeEnum = configuration.getDefaultResultSetType();
  }
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  String resultSets = context.getStringAttribute("resultSets");

  // 关键的一步: MappedStatement 的创建  封装成MappedStatement对象
  //每一个sql变成一个MappedStatement
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}


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  是namespace+sqlId判断最终的sql标签是否重复
  id = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

  MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
      .resource(resource)
      .fetchSize(fetchSize)
      .timeout(timeout)
      .statementType(statementType)
      .keyGenerator(keyGenerator)
      .keyProperty(keyProperty)
      .keyColumn(keyColumn)
      .databaseId(databaseId)
      .lang(lang)
      .resultOrdered(resultOrdered)
      .resultSets(resultSets)
      .resultMaps(getStatementResultMaps(resultMap, resultType, id))
      .resultSetType(resultSetType)
      .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
      .useCache(valueOrDefault(useCache, isSelect))
      .cache(currentCache);

  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }

  MappedStatement statement = statementBuilder.build();
  // 最关键的一步,在 Configuration 添加了 MappedStatement 
  configuration.addMappedStatement(statement);
  return statement;
}

public void addMappedStatement(MappedStatement ms) {
  //Id 是namespace+sqlId 也是这个map中的key 
  //key是 namespace+sqlId value是 MappedStatement 
  mappedStatements.put(ms.getId(), ms);
}

parse方法中的第二步bindMapperForNamespace()操作参考以下代码

private void bindMapperForNamespace() {
  //拿到namespace
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      //拿到相应的class对象
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      //判断configuration 是否有 如果没有 就添加到configuration中
      if (!configuration.hasMapper(boundType)) {
        configuration.addLoadedResource("namespace:" + namespace);
        // 添加到 MapperRegistry,本质是一个 map,里面也有 Configuration 
        configuration.addMapper(boundType);
      }
    }
  }
}


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 {
      // !Map<Class<?>, MapperProxyFactory<?>> 存放的是接口类型,和对应的工厂类的关系
      //key是namespace那个接口的对象 value 对应的代理工厂类
      knownMappers.put(type, new MapperProxyFactory<>(type));

      // 注册了接口之后,根据接口,开始解析所有方法上的注解,例如 @Select >>
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }

二、创建会话

拿到执行器(Excutor)如果没有传入执行器类型,默认就是SIMPLE,否则就是传入的类型。如果开启的缓存,将会把SimpleExecutor执行器封装成CachingExecutor创建会话最终就是为了得到SqlSession。

源码实现

@Test
public void testSelect() throws IOException {
    SqlSession session = sqlSessionFactory.openSession();
    try {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Blog blog = mapper.selectBlogById(1);
        System.out.println(blog);
    } finally {
        session.close();
    }
}
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);
    // 创建事务
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 根据事务工厂和默认的执行器类型,创建执行器 
    final Executor executor = configuration.newExecutor(tx, execType);
    //将得到的执行器放入到默认的sqlSession中  最后得到一个sqlSession
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx);
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

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 {
    // 默认 SimpleExecutor
    executor = new SimpleExecutor(this, transaction);
  }
  // 二级缓存开关,settings 中的 cacheEnabled 默认是 true
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // 植入插件的逻辑,至此,四大对象已经全部拦截完毕
  //缓存的执行器用到的是装饰器模式
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

简单举例上面提到的执行器

//传入执行器的方式 
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

public enum ExecutorType {
    //执行mapper.save()方法时,就相当于jdbc的stmt.execute(sql)
    SIMPLE,
    //会重用sql,通过stmt传入多项参数值
    REUSE,
    //相当于stmt.addBatch(sql)这样的,批量
    BATCH;

    private ExecutorType() {
    }
}

三、语句执行

通过接口的类型,在knownMappers中去获取代理工厂,然后创建代理类,代理类的handler是MapperProxy调用方法,走到MapperProxy中invoke方法,去掉object中的方法,直接调用。如果你在settings全局配置文件中配置了cacheEnabled为true,并且xml中配置了 标签,并且在sql语句上配置了useCache,就会调用CachingExecutor对象的query方法CachingExecutor类中的query方法会先看二级缓存中有没有,如果有直接返回,如果没有,会去走到SimpleExecutor的query方法最后就和调用JDBC的流程一样,打开连接,查询数据,返回数据。

源码实现

@Test
public void testSelect() throws IOException {
    SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); // ExecutorType.BATCH
    try {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        //走到代理类的invoke对象
        Blog blog = mapper.selectBlogById(1);
        System.out.println(blog);
    } finally {
        session.close();
    }
}

从knownMappers中拿到代理对象

public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  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);
  }
}

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
  // 1:类加载器:2:被代理类实现的接口、3:实现了 InvocationHandler 的触发管理类
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

执行该方法会走到代理类的invoke对象
Blog blog = mapper.selectBlogById(1);

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    // toString hashCode equals getClass等方法,无需走到执行SQL的流程
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else {
      // 提升获取 mapperMethod 的效率,先到 MapperMethodInvoker(内部接口) 的 invoke
      // 普通方法会走到 PlainMethodInvoker(内部类) 的 invoke
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

interface MapperMethodInvoker {
  Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}


private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  try {
    // JAVA8 中 Map 的方法,根据 key 获取值,如果值是 null,则把后面Object 的值赋给 key
    // 如果获取不到,就创建
    // 获取的是 MapperMethodInvoker(接口) 对象,只有一个invoke方法
    return methodCache.computeIfAbsent(method, m -> {
      if (m.isDefault()) {
        // 接口的默认方法(Java8),只要实现接口都会继承接口的默认方法,例如 List.sort()
        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 {
        // 创建了一个 MapperMethod
        return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
    });
  } catch (RuntimeException re) {
    Throwable cause = re.getCause();
    throw cause == null ? re : cause;
  }
}

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

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
  // SQL执行的真正起点
  return mapperMethod.execute(sqlSession, args);
}

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      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 if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        // 普通 select 语句的执行入口 >>
        result = sqlSession.selectOne(command.getName(), param);
        if (method.returnsOptional()
            && (result == null || !method.getReturnType().equals(result.getClass()))) {
          result = Optional.ofNullable(result);
        }
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      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;
}


//举个查询多条的例子
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
  List<E> result;
  Object param = method.convertArgsToSqlCommandParam(args);
  if (method.hasRowBounds()) {
    RowBounds rowBounds = method.extractRowBounds(args);
    result = sqlSession.selectList(command.getName(), param, rowBounds);
  } else {
    result = sqlSession.selectList(command.getName(), param);
  }
  // issue #510 Collections & arrays support
  if (!method.getReturnType().isAssignableFrom(result.getClass())) {
    if (method.getReturnType().isArray()) {
      return convertToArray(result);
    } else {
      return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    }
  }
  return result;
}



@Override
public <E> List<E> selectList(String statement, Object parameter) {
  // 为了提供多种重载(简化方法使用),和默认值
  // 让参数少的调用参数多的方法,只实现一次
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
      //从configuration中获取MappedStatement 这是在初始化的时候存进去的
      //这里是通过namespace+sqlId来进行获取的
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 如果 cacheEnabled = true(默认),Executor会被 CachingExecutor装饰
    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();
  }
}

//我们在这里看的是走缓存的
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 获取SQL
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  // 创建CacheKey:什么样的SQL是同一条SQL? 
  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();
  // cache 对象是在哪里创建的?  XMLMapperBuilder类 xmlconfigurationElement()
  // 由 <cache> 标签决定
  if (cache != null) {
    // flushCache="true" 清空一级二级缓存 >>
    flushCacheIfRequired(ms);
    //是否使用缓存
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      // 获取二级缓存
      // 缓存通过 TransactionalCacheManager、TransactionalCache 管理
      @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;
    }
  }
  // 走到 SimpleExecutor | ReuseExecutor | BatchExecutor
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

//没有走缓存的 ,是直接查询数据库的操作
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  // 异常体系之 ErrorContext
  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()) {
    // flushCache="true"时,即使是查询,也清空一级缓存
    clearLocalCache();
  }
  List<E> list;
  try {
    // 防止递归查询重复处理缓存
    queryStack++;
    // 查询一级缓存
    // ResultHandler 和 ResultSetHandler的区别
    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();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  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 {
    // 三种 Executor 的区别,看doUpdate
    // 默认Simple
    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;
}

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
      //拿到MappedStatement 中的所有信息
    Configuration configuration = ms.getConfiguration();
    // 注意,已经来到SQL处理的关键对象 StatementHandler 
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    // 获取一个 Statement对象 详情看下方代码
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 执行查询
    return handler.query(stmt, resultHandler);
  } finally {
    // 用完就关闭
    closeStatement(stmt);
  }
}

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  //这里进行的预编译 详情看下方代码
  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) {
  // StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPARED
  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      // 创建 StatementHandler 的时候做了什么? >>
      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());
  }
}


private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  // 获取 Statement 对象
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 为 Statement 设置参数
  handler.parameterize(stmt);
  return stmt;
}

//执行查询 - 执行sql   handler.query(stmt, resultHandler);
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 到了JDBC的流程
  ps.execute();
  // 处理结果集 并返回
  return resultSetHandler.handleResultSets(ps);
}

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

  final List<Object> multipleResults = new ArrayList<>();

  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);
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    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);
}

到此我们就已经阅读完mybatis从初始化到创建会话最后到执行查询的原理了。。。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>