跳到主要内容
  1. 所有文章/
  2. 《Mybatis3源码深度解析》笔记/

Mybatis源码-SqlSession执行Mapper过程

·📄 6420 字·🍵 13 分钟

Mapper由两部分组成,分别为Mapper接口和通过注解或者XML文件配置的SQL语句。主要分为4个部分讲解:

  1. Mapper接口的注册过程
  2. MappedStatement对象的注册过程
  3. Mapper方法的调用过程
  4. SqlSession执行Mapper的过程

Mapper接口的注册过程 #

// 获取UserMapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行Mapper方法,获取执行结果
List<UserEntity> userList = userMapper.listAllUser();

我们知道,接口中定义的方法必须通过某个类实现该接口,然后创建该类的实例,才能通过实例调用方法。所以 SqlSession对象的getMapper()方法返回的一定是某个类的实例。getMapper()方法返回的是一个动态代理对象。

MyBatis中通过MapperProxy类实现动态代理。MapperProxy使用的是JDK内置的动态代理,实现了InvocationHandler接口,invoke()方法中为通用的拦截逻辑,具体内容在介绍Mapper方法调用过程时再做介绍。

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 if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 对Mapper接口中定义的方法进行封装,生成MapperMethod对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
}

使用JDK内置动态代理,通过MapperProxy类实现InvocationHandler接口,定义方法执行拦截逻辑后,还需要调用java.lang.reflect.Proxy类的newProxyInstance()方法创建代理对象。MyBatis对这一过程做了封装,使用MapperProxyFactory创建Mapper动态代理对象。

MapperProxyFactory代码如下:

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

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

}

MapperProxyFactory类的工厂方法newInstance()是非静态的。也就是说,使用MapperProxyFactory 创建Mapper 动态代理对象首先需要创建MapperProxyFactory实例。MapperProxyFactory实例是什么时候创建的呢?

MyBatis通过mapperRegistry属性注册Mapper接口与MapperProxyFactory对象之间的对应关系。下面是MapperRegistry类的关键代码:

public class MapperRegistry {
  // Configuration对象引用
  private final Configuration config;
  // 用于注册Mapper接口Class对象,和MapperProxyFactory对象对应关系
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }
  // 根据Mapper接口Class对象获取Mapper动态代理对象
  @SuppressWarnings("unchecked")
  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> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }
  
  // 根据Mapper接口Class对象,创建MapperProxyFactory对象,并注册到knownMappers属性中
  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
  // ..
}

MyBatis 框架在应用启动时会解析所有的Mapper接口,然后调用MapperRegistry 对象的addMapper()方法将Mapper接口信息和对应的MapperProxyFactory对象注册到MapperRegistry对象中。

MappedStatement对象的注册过程 #

MyBatis通过MappedStatement类描述Mapper的SQL配置信息。SQL配置有两种方式:一种是通过XML文件配置;另一种是通过Java注解,而Java注解的本质就是一种轻量级的配置信息。

Configuration中有一个mappedStatements 属性,用于注册所有的MappedStatement对象

protected final Map<String, MappedStatement> mappedStatements = 
new StrictMap<MappedStatement>("Mapped Statements collection");

mappedStatements属性是一个Map对象,它的Key为Mapper SQL配置的Id,如果SQL是通过XML配置的,则 Id为命名空间加上<select/update/delete/insert>标签的 Id,如果 SQL通过Java注解配置,则Id为Mapper接口的完全限定名(包括包名)加上方法名称。

想要了解MappedStatement对象的创建过程,就必须重点关注<mappers>标签的解析过程。<mappers>标签是通过XMLConfigBuilder 类的mapperElement()方法来解析的。下面是mapperElement()方法的实现:

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // 通过<package>标签指定包名
      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");
        // 通过resource属性指定XML文件路径
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          // 通过url属性指定XML文件路径
          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.");
        }
      }
    }
  }
}

对应如下几种方式的mapper定义:

<mapper resourcer="./>这种形式为例介绍MapperSQL配置文件的解析过程。

Mapper SQL配置文件的解析需要借助XMLMapperBuilder对象。在mapperElement()方法中,首先创建一个XMLMapperBuilder对象,然后调用XMLMapperBuilder对象的parse()方法完成解析,该方法内容如下:

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // 调用XPathParser的evalNode()方法获取根节点对应的XNode对象
    configurationElement(parser.evalNode("/mapper"));
    // 將资源路径添加到Configuration对象中
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }
  // 继续解析之前解析出现异常的ResultMap对象
  parsePendingResultMaps();
  // 继续解析之前解析出现异常的CacheRef对象
  parsePendingCacheRefs();
  // 继续解析之前解析出现异常<select|update|delete|insert>标签配置
  parsePendingStatements();
}

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");
    }
    // 设置当前正在解析的Mapper配置的命名空间
    builderAssistant.setCurrentNamespace(namespace);
    // 解析<cache-ref>标签
    cacheRefElement(context.evalNode("cache-ref"));
    // 解析<cache>标签
    cacheElement(context.evalNode("cache"));
    // 解析所有的<parameterMap>标签
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // 解析所有的<resultMap>标签
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // 解析所有的<sql>标签
    sqlElement(context.evalNodes("/mapper/sql"));
    // 解析所有的<select|insert|update|delete>标签
    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);
  }
}

// 重点关注解析所有的<select|insert|update|delete>标签
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    // 通过XMLStatementBuilder对象,对<select|update|insert|delete>标签进行解析
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      // 调用parseStatementNode()方法解析
      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;
    }
    // 解析<select|update|delete|insert>标签属性
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    // 获取LanguageDriver对象
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    // 获取Mapper返回结果类型Class对象
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    // 默认Statement类型为PREPARED
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType",
            StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

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

    // 將<include>标签内容,替换为<sql>标签定义的SQL片段
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // 解析<selectKey>标签
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // 通过LanguageDriver解析SQL内容,生成SqlSource对象
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    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;
    }

    // MapperBuilderAssistant对象用于生成 MappedStatement 对象
    // 最终会调用 configuration.addMappedStatement(statement);方法
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

Mapper方法的调用过程 #

Mapper代理对象MapperProxy的invoke执行过程:

@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 if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  // 对Mapper接口中定义的方法进行封装,生成MapperMethod对象
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
  
private MapperMethod cachedMapperMethod(Method method) {
  MapperMethod mapperMethod = methodCache.get(method);
  if (mapperMethod == null) {
    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
    methodCache.put(method, mapperMethod);
  }
  return mapperMethod;
}

MapperMethod类是对Mapper方法相关信息的封装,通过MapperMethod能够很方便地获取SQL语句的类型、方法的签名信息等。

public class MapperMethod {
  private final SqlCommand command;
  private final MethodSignature method;
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
      this.command = new SqlCommand(config, mapperInterface, method);
      this.method = new MethodSignature(config, mapperInterface, method);
  }
  // ... 
}

// SqlCommand的解析和创建过程
public static class SqlCommand {
  private final String name; // Mapper Id
  private final SqlCommandType type; // SQL类型
  public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      final String methodName = method.getName();
      // 获取声明该方法的类或接口的Class对象
      final Class<?> declaringClass = method.getDeclaringClass();
      // 获取描述<select|update|insert|delete>标签的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);
          }
      }
  }
  
  private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
                                                       Class<?> declaringClass, Configuration configuration) {
        // 获取Mapper的Id
        String statementId = mapperInterface.getName() + "." + methodName;
        if (configuration.hasStatement(statementId)) {
            // 如果Configuration对象中已经注册了MappedStatement对象,则获取该MappedStatement对象
            return configuration.getMappedStatement(statementId);
        } else if (mapperInterface.equals(declaringClass)) {
            return null;
        }
        // 如果方法是在Mapper父接口中定义,则根据父接口获取对应的MappedStatement对象
        for (Class<?> superInterface : mapperInterface.getInterfaces()) {
            if (declaringClass.isAssignableFrom(superInterface)) {
                MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                        declaringClass, configuration);
                if (ms != null) {
                    return ms;
                }
            }
        }
        return null;
    }

}

// MethodSignature的解析和创建过程
//(1)获取Mapper方法的返回值类型,具体是哪种类型,通过boolean类型的属性进行标记。
//(2)记录RowBounds参数位置,用于处理后续的分页查询,同时记录ResultHandler参数位置,用于处理从数据库中检索的每一行数据。
//(3)创建ParamNameResolver对象。ParamNameResolver对象用于解析Mapper方法中的参数名称及参数注解信息。
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();
    }
    // 返回值类型为void
    this.returnsVoid = void.class.equals(this.returnType);
    // 返回值类型为集合
    this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
    // 返回值类型为Cursor
    this.returnsCursor = Cursor.class.equals(this.returnType);
    // 返回值类型为Optional
    this.returnsOptional = Jdk.optionalExists && Optional.class.equals(this.returnType);
    this.mapKey = getMapKey(method);
    // 返回值类型为Map
    this.returnsMap = this.mapKey != null;
    // RowBounds参数位置索引
    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
    // ResultHandler参数位置索引
    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
    // ParamNameResolver用于解析Mapper方法参数
    this.paramNameResolver = new ParamNameResolver(configuration, method);
}

在ParamNameResolver构造方法中,对所有Mapper方法的所有参数信息进行遍历,首先判断参数中是否有@Param注解,如果包含@Param注解,就从注解中获取参数名称,如果参数中没有@Param注解,就根据MyBatis主配置文件中的useActualParamName参数确定是否获取实际方法定义的参数名称,若useActualParamName参数值为true,则使用方法定义的参数名称。解析完毕后,将参数信息保存在一个不可修改的names 属性中,该属性是一个SortedMap<Integer,String>类型的对象。

public ParamNameResolver(Configuration config, Method method) {
  // 获取所有参数类型
  final Class<?>[] paramTypes = method.getParameterTypes();
  // 获取所有参数注解
  final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
  int paramCount = paramAnnotations.length;
  // 从@Param 注解中获取参数名称
  for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
    if (isSpecialParameter(paramTypes[paramIndex])) {
      continue;
    }
    String name = null;
    for (Annotation annotation : paramAnnotations[paramIndex]) {
      // 方法参数中,是否有Param注解
      if (annotation instanceof Param) {
        hasParamAnnotation = true;
        // 获取参数名称
        name = ((Param) annotation).value();
        break;
      }
    }
    if (name == null) {
      // 未指定@Param 注解,这判断是否使用实际的参数名称,参考useActualParamName属性的作用
      if (config.isUseActualParamName()) {
        // 获取参数名
        name = getActualParamName(method, paramIndex);
      }
      if (name == null) {
        name = String.valueOf(map.size());
      }
    }
    // 將参数信息存放在Map中,Key为参数位置索引,Value为参数名称
    map.put(paramIndex, name);
  }
  // 將参数信息保存在names属性中
  names = Collections.unmodifiableSortedMap(map);
}

MapperMethod对象创建完成之后,会调用execute()方法。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 其中command为MapperMethod构造是创建的SqlCommand对象
    // 获取SQL语句类型
    switch (command.getType()) {
        case INSERT: {
            // 获取参数信息
            Object param = method.convertArgsToSqlCommandParam(args);
            // 调用SqlSession的insert()方法,然后调用rowCountResult()方法统计行数
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
        }
        case UPDATE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            // 调用SqlSession对象的update()方法
            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);
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional() &&
                        (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = OptionalUtil.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;
}

SqlSession执行Mapper的过程 #

上一节介绍了Mapper方法的调用过程。我们了解到,**MyBatis通过动态代理将Mapper方法的调用转换为调用SqlSession提供的增删改查方法,以Mapper的Id作为参数,执行数据库的增删改查操作。以SqlSession.selectList()**追踪

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    // 根据Mapper的Id,获取对应的MappedStatement对象
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 以MappedStatement对象作为参数,调用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的query()方法,这里使用了一级缓存
// org.apache.ibatis.executor.BaseExecutor#createCacheKey可以看看缓存的key是怎么创建的。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 获取BoundSql对象,BoundSql是对动态SQL解析生成的SQL语句和参数映射信息的封装
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 创建CacheKey,用于缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    // 调用重载的query()方法
    return query(ms, parameter, 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 {
      // 缓存中获取不到,则调用queryFromDatabase()方法从数据库中查询
      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 {
    // 调用doQuery()方法查询
    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;
}

// SimpleExecutor.doQuery()
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);
    // 调用prepareStatement()方法,创建Statement对象,并进行设置参数等操作
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 调用StatementHandler对象的query()方法执行查询操作
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

// SimpleStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  String sql = boundSql.getSql();
  // 真正调用JDBC的API
  statement.execute(sql);
  return resultSetHandler.<E>handleResultSets(statement);
}