14.mybatis源码解析

发布于:2025-04-04 ⋅ 阅读:(37) ⋅ 点赞:(0)

简介

mybatis是一个持久层框架,负责和数据库交互,它的内部封装了jdbc。用户通过使用mybatis框架,仅仅只需要提供mybatis的配置文件和sql,就可以实现和数据库的交互,不再需要编写获取数据库连接、执行sql、解析返回值等代码,这些任务统统被交给了mybatis。

使用mybatis的整体流程,就是读取并解析配置文件,然后创建SqlSession实例,通过SqlSession来调用mapper接口,执行sql。这里介绍两个关键点:mybatis是如何启动的、sql是如何执行的。

mybatis的启动

mybatis的启动过程,就是读取并解析配置文件,然后创建SqlSession实例的过程

相关组件

Resources

mybatis提供的工具类,用于加载外部的资源文件,它的内部封装了类加载器,通过类加载器提供的api来加载资源。

public class Resources {
  // 类加载器的包装类
  private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
  
  // 调用类加载器提供的API来加载外部资源
  public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return in;
  }
}

// 类加载器的包装类
public class ClassLoaderWrapper {

  ClassLoader defaultClassLoader;
  ClassLoader systemClassLoader;
  
  // 当前类中可以获取到的类加载器
  ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }
}

SqlSessionFactoryBuilder

建造者模式,创建SqlSessionFactory实例

public class SqlSessionFactoryBuilder {

}

SqlSessionFactory

工厂模式,创建SqlSession实例

SqlSessionFactory接口:

public interface SqlSessionFactory {

  // 使用默认配置,创建一个SqlSession对象
  SqlSession openSession();
  
  // 获取配置信息
  Configuration getConfiguration();
}

SqlSession

SqlSession是mybatis的顶层接口,用户通过操作SqlSession来操作mybatis,它定义了获取连接、获取配置信息、执行sql语句的方法

public interface SqlSession extends Closeable {

  /*
   *  执行sql语句的方法
   */
  <T> T selectOne(String statement, Object parameter);

  <E> List<E> selectList(String statement, Object parameter);

  int insert(String statement, Object parameter);

  int update(String statement, Object parameter);

  int delete(String statement, Object parameter);

  void commit(boolean force);

  void rollback(boolean force);

  // 关闭当前SqlSession
  @Override
  void close();

  // 清理缓存
  void clearCache();

  // 获取配置信息
  Configuration getConfiguration();

  // 获取mapper接口
  <T> T getMapper(Class<T> type);

  // 获取连接
  Connection getConnection();

}

总结:这里大致介绍了从加载配置文件到创建SqlSession的过程中的相关组件。

  • mybatis使用类加载器提供的API来加载外部资源,
  • 使用建造者模式来创建一个SqlSession工厂,建造者默认负责解析配置文件,根据用户提供的参数,来创建一个SqlSession工厂
  • SqlSession工厂负责创建SqlSession

整体流程

第一步:SqlSessionFactoryBuilder

解析配置文件,创建SqlSessionFactory

// 1、build方法:核心创建方法,参数1是配置文件的输入流,参数2指定使用哪个环境
public SqlSessionFactory build(InputStream inputStream, String environment, 
                               Properties properties) {
  try {
    // 解析配置文件
    XMLConfigBuilder parser = 
        new XMLConfigBuilder(inputStream, environment, properties);
    // 构建SqlSessionFactory的实例
    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.
    }
  }
}

// 2、上一步中调用的重载的build方法,这里已经解析好了配置文件,现在创建SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

第二步:SqlSessionFactory

创建SqlSession,这里暂时不关注SqlSession中的组件,只关注它的建造流程,组件在随后学习sql执行的时候再看

// 参数1 execType,枚举值 SIMPLE, REUSE, BATCH,简单、重用、批量,默认是简单
// 参数2 事务隔离级别
private SqlSession openSessionFromDataSource(ExecutorType execType, 
                                             TransactionIsolationLevel level, 
                                             boolean autoCommit) {
  Transaction tx = null;
  try {
    // 从配置信息中,获取environments标签中存储的环境信息
    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);
    // 传入Configuration实例、Executor实例,创建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();
  }
}

总结:

1、创建SqlSession的过程,分别用到了建造者模式和工厂模式,为什么要选择这两种模式?

工厂模式和建造者模式的区别在于,工厂模式,用户不需要知道对象的创建过程,建造者模式,用户需要使用建造者提供的API来手动编排对象的创建过程。这里对应建造者模式和工厂模式的使用,我感觉很容易让人混淆,它们都是传入多个配置参数,创建一个实例,感觉可以互相替代。

解析配置文件

在之前学习创建SqlSessionFactory的时候,提到了解析配置文件,但是并没有深入学习,在这里,深入地学习mybatis是如何把配置文件中的信息解析到内存中。

相关组件

文件解析器

BaseBuilder: 文件解析器的公共父类

public abstract class BaseBuilder {
  // 存储解析完的配置信息
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
}

主配置文件解析器 XMLConfigBuilder:

public class XMLConfigBuilder extends BaseBuilder {

  private boolean parsed;             // 解析完成后这个标志置为true
  private final XPathParser parser;   // 解析器
  private String environment;         // 使用哪个环境
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
}

mapper.xml文件解析器 XMLConfigBuilder,和主配置文件解析器相同的布局

public class XMLConfigBuilder extends BaseBuilder {

  private boolean parsed;
  private final XPathParser parser;
  private String environment;
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
}

XML文件解析器 XPathParser:这个是最基础的组件,负责解析xml文件,需要用到第三方库提供的API

public class XPathParser {

  private final Document document;  // xml文件在内存中的实例
  private boolean validation;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;
}

整体流程

功能入口

创建解析主配置文件的解析器:XMLConfigBuilder

// SqlSessionFactoryBuilder类:使用建造者模式来创建SqlSessionFactory对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 创建配置文件解析器
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 解析配置文件并且创建SqlSessionFactory对象
    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.
    }
  }
}

第一步:解析主配置文件

XMLConfigBuilder的构造方法:

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
  // XPathParser:负责把xml文件转换为document对象
  this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), 
       environment, props);
}
public XPathParser(InputStream inputStream, boolean validation, 
                   Properties variables, EntityResolver entityResolver) {
  commonConstructor(validation, variables, entityResolver);
  // 将xml文件解析为document对象
  this.document = createDocument(new InputSource(inputStream));
}

把输入流解析为document对象的方法:

public Configuration parse() {
  // 判断是否已经解析过
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // <configuration>标签是主配置文件中的根节点,在这里解析出根节点后,
  // 再调用parseConfiguration方法,解析根节点下的每个子节点
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

解析根节点下的每个子节点:

// 解析根节点下的每一个节点,解析完的结果会存在configuration对象中
private void parseConfiguration(XNode root) {
  try {
    //issue #117 read properties first
    // 解析properties节点
    propertiesElement(root.evalNode("properties"));
    // 解析settings节点
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);
    // 解析typeAliases节点
    typeAliasesElement(root.evalNode("typeAliases"));
    // 解析plugins节点
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析mappers节点
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

解析完节点中的信息后,会把解析结果保存到Configuration对象中,这里以解析typeAliases节点、解析environment节点、解析mappers节点为例。

解析typeAliases节点

typeAliases节点的案例

<typeAliases>
    <typeAlias type="org.wyj.beans.User" alias="user" />
</typeAliases>

解析typeAliases节点的源码:

// 解析typeAliases节点
private void typeAliasesElement(XNode parent) {
  if (parent != null) {
    // 遍历typeAliases节点下的子节点
    for (XNode child : parent.getChildren()) {
      // 判断typeAliases节点的子节点的名称是否等于package
      if ("package".equals(child.getName())) {
        String typeAliasPackage = child.getStringAttribute("name");
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } else {
        // 如果子节点的名称不是package,一定是typeAliase,获取type属性和alias属性
        String alias = child.getStringAttribute("alias");
        String type = child.getStringAttribute("type");
        try {
          // 调用别名注册器,注册类对象和别名之间的对应关系
          Class<?> clazz = Resources.classForName(type);
          if (alias == null) {
            typeAliasRegistry.registerAlias(clazz);
          } else {
            typeAliasRegistry.registerAlias(alias, clazz);
          }
        } catch (ClassNotFoundException e) {
          throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
        }
      }
    }
  }
}

别名注册器 TypeAliasRegistry:

// TypeAliasRegistry类
public void registerAlias(Class<?> type) {
  // Class类中的getSimleName方法,获取到的是类名
  String alias = type.getSimpleName();
  // 如果一个类被@Alias注解标注,使用@Alias注解中指定的值,否则默认使用类名来作为类的别名
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  }
  registerAlias(alias, type);
}
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();

public void registerAlias(String alias, Class<?> value) {
  if (alias == null) {
    throw new TypeException("The parameter alias cannot be null");
  }
  // issue #748
  String key = alias.toLowerCase(Locale.ENGLISH);
  if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null 
      && !TYPE_ALIASES.get(key).equals(value)) {
    throw new TypeException("The alias '" + alias + 
                            "' is already mapped to the value '" + 
                            TYPE_ALIASES.get(key).getName() + "'.");
  }
  // 将别名和类对象保存到TypeAliasRegistry类的实例中
  TYPE_ALIASES.put(key, value);
}

解析environments节点

environments节点的配置案例:

<environments default="develop" >
    <environment id="develop" >
        <transactionManager type="JDBC" />
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

解析environments节点的源码

// XMLConfigBuilder类
private void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    if (environment == null) {
      // 获取environments标签的default属性
      environment = context.getStringAttribute("default");
    }
    // 遍历environments标签中的子节点
    for (XNode child : context.getChildren()) {
      // 获取environment标签的id属性
      String id = child.getStringAttribute("id");
      // 如果它和default属性中指定的值是一致的,进行if逻辑
      if (isSpecifiedEnvironment(id)) {
        // 根据transactionManager标签,配置事务管理器
        TransactionFactory txFactory = 
            transactionManagerElement(child.evalNode("transactionManager"));
        // 根据dataSource标签,配置数据库连接池工厂
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        // 创建数据库连接池
        DataSource dataSource = dsFactory.getDataSource();
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        // 将配置信息注册到configuration对象中
        configuration.setEnvironment(environmentBuilder.build());
      }
    }
  }
}
解析mappers节点

mappers节点的案例:

<mappers>
    <mapper resource="userMapper.xml"/>
</mappers>

解析mappers节点的源码:

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 遍历mappers节点下的所有子节点
    for (XNode child : parent.getChildren()) {
      // 如果是package标签,获取并且加载package下所有的文件
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        // 如果子节点的名称不等于package,则子节点的名称一定等于mapper
        // 获取resource、url、class属性,这三个属性只有一个有值
        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);
          // 解析mapper.xml配置文件
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(
              inputStream, configuration, resource, configuration.getSqlFragments());
          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) {
          // 如果mapperClass属性不为空
          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.");
        }
      }
    }
  }
  // 最终,不管哪个属性,都是把解析到的信息注册到configuration对象中
}

第二步:解析mapper.xml

解析mapper文件的解析器:XMLMapperBuilder

// XMLMapperBuilder类
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // 解析mapper文件中的标签
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

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

解析mapper文件中的标签:

private void configurationElement(XNode context) {
  try {
    // namespace属性
    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"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // sql标签
    sqlElement(context.evalNodes("/mapper/sql"));
    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节点
// XMLStatementBuilder类
public void parseStatementNode() {
  // 获取select标签中的所有属性
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

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

  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");
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  Class<?> resultTypeClass = resolveClass(resultType);
  String resultSetType = context.getStringAttribute("resultSetType");
  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 Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

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

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  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;
  }

  // 最终把数据保存到configuration对象的mappedStatements属性中,id是配置文件的namespace加标签id,值是select语句
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

把MappedStatement保存到configuration实例中:

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

  // sql语句的id实际是上接口名 + 方法名
  id = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

  // 生成mappedStatement,它存储了关于一条sql的全部信息
  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实例中
  configuration.addMappedStatement(statement);
  return statement;
}

总结

解析配置文件的大概步骤,依据配置文件中设定好的标签和属性,依次解析。xml文件中的每个标签,都会对应一个实体类。解析完的结果存放到配置类中。需要用到第三方库提供的API来解析xml文件。

SQL的执行流程

整体流程:通过SqlSession的实例,获取mapper接口的实现类,这里的实现类是mybatis通过动态代理技术创建的,通过这个实现类来调用mapper.xml文件中配置的sql语句,执行sql。

过程拆解:

  • 创建mapper接口的代理类,也可以理解为mapper接口的实现类
  • 通过代理类调用sqlSession
  • 通过sqlSession来执行sql

创建Mapper接口的实现类

mybatis会使用jdk提供的动态代理技术,创建Mapper接口的实现类

相关组件

mapper接口注册器 MapperRegistry

MapperRegistry:存储了mapper接口和它的代理类工厂之间的对应关系,代理类工厂负责创建mapper接口的代理类

public class MapperRegistry {
  private final Configuration config;
  // mapper接口的类对象和它的工厂类,工厂类负责创建代理类
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
}
代理类的工厂类 MapperProxyFactory

MapperProxyFactory:一个工厂类实例对应一个mapper接口,它负责创建这个mapper接口的代理类

public class MapperProxyFactory<T> {
  // mapper接口
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
}
代理类的InvocationHandler MapperProxy

mybatis使用jdk提供的技术来创建代理类,代理类中调用InvocationHandler来实现增强功能

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;              // mapper接口
  private final Map<Method, MapperMethod> methodCache; // 缓存接口中解析好的方法
}

整体流程

代理类是如何被创建的?

mapper注册器

1、在解析配置文件的时候,mybatis会把mapper接口和mapperProxyFactory之间的对应关系存储到配置类的mapper注册器中

加载Mapper接口的类对象:这一步发生在解析mapper.xml的时候

private void bindMapperForNamespace() {
  // 获取namesapce
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      // namespace中的值是Mapper接口的类对象,这里加载类对象
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      if (!configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:" + namespace);
        // 注册mapper接口到配置类中
        configuration.addMapper(boundType);
      }
    }
  }
}

注册Mapper接口到配置类中:这里把配置文件中解析出的mapper接口的全限定名注册到配置类中

// Configuration类
public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

// mapperRegistry:实例化MapperProxyFactory,并且创建它和Mapper接口的对应关系:
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 {
      // 实例化MapperProxyFactory,并且注册它和Mapper接口的对应关系
      knownMappers.put(type, new MapperProxyFactory<>(type));
      // 解析mapper接口
      // 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);
      }
    }
  }
}

这里会把mapper接口和MapperProxyFactory之间的对应关系存储到mapperRegistry中,配置类持有mapperRegistry的实例。

动态代理技术

2、用户通过SqlSession来创建mapper接口时,使用动态代理技术创建一个代理类。调用SqlSession中的API来创建代理类,SqlSession持有configuration的实例,configuration持有mapperRegistry的实例,逐级调用,直到mapperRegistry中的getMapper方法

public class MapperRegistry {
  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();    

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 获取Mapper接口对应的MapperProxyFactory
    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);
    }
  }
}

MapperProxyFactory:MapperProxy的工厂类, 它用于创建Mapper接口的动态代理类,MapperProxy相当于动态代理类的InvocationHandler

// mapperProxyFactory中的newInstance方法:
public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = 
      new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  // 调用重载的newInstance方法
  return newInstance(mapperProxy);
}

// 重载的newInstance方法
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
  // 底层是基于jdk提供的技术,在这里应该关注MapperProxy类,它是InvocationHandler接口的实现类
  // 动态生成的代理类会执行MapperProxy类中的代码
  return (T) Proxy
      .newProxyInstance(mapperInterface.getClassLoader(), 
                        new Class[] { mapperInterface }, mapperProxy);
}

总结

代理类的创建过程:

  • 在解析配置文件时,把mapper接口和它的工厂类放到一个注册器中
  • 创建mapper接口的代理类时,调用它对应的工厂类,创建一个代理类实例,底层使用jdk提供的动态代理技术
  • 代理类的创建使用了工厂模式,一个mapper接口对应一个工厂实例

调用mapper接口的实现类

相关组件

MapperMethod

封装了Mapper接口中单个方法的反射信息,主要是方法的返回值类型和sql的类型,通过这些信息来决定下一步调用哪个API执行任务。

public class MapperMethod {
  // SqlCommand,内部类,存储sql的类型,insert、select等
  private final SqlCommand command;
  // MethodSignature,内部类,存储方法的签名
  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);
  }

  public static class SqlCommand {
    private final String name;          // sql的id
    private final SqlCommandType type;  // sql类型,枚举值 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
  }

  public static class MethodSignature {
    // 方法的返回值类型,用于根据返回值来决定调用哪个方法执行sql
    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final boolean returnsCursor;
    private final boolean returnsOptional;
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final ParamNameResolver paramNameResolver;  // 参数解析器,解析@Param注解
  }
}

它有两个内部类 SqlCommand、MethodSignature,同时它持有这两个内部类的实例,这两个实例中都存储了Mapper接口中的方法的信息

整体流程

这里涉及到jdk动态代理的执行机制:代理类会执行InvocationHandler接口中的invoke方法,在这里实现InvocationHandler接口的是MapperProxy。所以代理类从MapperProxy的invoke方法开始执行。

public class MapperProxy<T> implements InvocationHandler, Serializable {
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  // 缓存解析好的方法
  private final Map<Method, MapperMethod> methodCache;

  // 代理类执行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);
      // 如果是接口中的default方法,直接调用,接口中的default方法是public、非抽象的的实例方法
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 生成并且缓存MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 调用MapperMethod中的方法,执行sql
    return mapperMethod.execute(sqlSession, args);
  }

  // 生成并且缓存MapperMethod
  private MapperMethod cachedMapperMethod(Method method) {
    // 如果缓存中没有方法,创建MapperMethod实例,然后放到缓存中
    return methodCache
        .computeIfAbsent(method, k -> new MapperMethod(
            mapperInterface, method, sqlSession.getConfiguration()));
  }
}

MapperMethod中执行sql语句的方法:

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  // 根据sql语句的类型,选择不同的方法,command.getType,获取到的就是标签类型,
  // 例如select、update等
  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:
      // select语句,依据返回值,选择不同的方法
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        // 返回值是一个list
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        // 返回值是一个map
        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 = 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;
  // 获取sql语句的参数,这里会解析@Param标签,把参数的名称和值封装到Object中
  Object param = method.convertArgsToSqlCommandParam(args);
  if (method.hasRowBounds()) {  // rowRound,如果没有特殊配置,就是没有
    RowBounds rowBounds = method.extractRowBounds(args);
    result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
  } else {
    // 调取sqlSession的selectList方法
    result = sqlSession.<E>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;
}

总结

用户调用mapper接口,通过mapper接口的代理类来调用SqlSession,实际是通过SqlSession来执行sql的。

mapper接口的代理类,实际上是一种优化措施,使用面向对象的方式来执行sql,所以代理类的实现中,最主要做的事情,就是解析mapper接口中的方法,通过解析结果,来决定调用sqlSession中的哪些api,例如,select语句,调用sqlSession中的selectList方法,

实现类调用SqlSession

sqlSession,最核心的API,它包含了最核心的流程

执行过程中涉及到的组件:

  • 执行器:executor,负责执行sql,同时实现缓存功能,调用sqlSrouce解析sql,调研statementHandler执行sql
  • sqlSource:负责解析sql,把sql中的mybatis参数替换为问号,例如,select * from user where id = #{id},会被解析为select * from user where id = ?,同时获取参数的值,以及其它相关信息,包括jdbctype、javaType等,这里还会负责解析动态sql
  • 语句处理器:statementHandler,负责处理statement实例,设置参数,执行sql语句,解析返回值
  • 参数处理器:parameterHandler,语句处理器调用参数处理器来为statement实例设置参数
  • 返回值解析器:resultSetHandler:语句处理器调用返回值解析器来解析statement实例返回的resultSet

相关组件

执行器 Executor

执行器,负责执行sql语句

1、顶层接口:Executor

public interface Executor {
  // update语句
  int update(MappedStatement ms, Object parameter) throws SQLException;

  // 查询语句
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  
  // 事务相关
  void commit(boolean required) throws SQLException;
  void rollback(boolean required) throws SQLException;
}

2、接口的实现类:执行器采用了模板方法设计模式,父类负责流程编排,子类负责具体事项,在这里,父类是BaseExecutor,实现了一级缓存和流程编排,子类负责具体的查询任务,这里子类以SimpleExectuor为例。

BaseExecutor:

public abstract class BaseExecutor implements Executor {
  protected Transaction transaction;  // 数据库连接
  protected Executor wrapper;         // 这里相当于this,它持有自己的实例

  protected PerpetualCache localCache;  // 一级缓存(SqlSession级别)
  
  // 交给子类实现的扩展点
  protected abstract int doUpdate(MappedStatement ms, Object parameter)
      throws SQLException;

  protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
      throws SQLException;

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;

}

SimpleExecutor:实现了具体的查询等功能

public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
}
SqlSource

负责解析mybatis参数和动态sql相关标签,返回BoundSql,BoundSql就是可以在数据库中执行的sql了。

1、顶层接口:SqlSource接口:

public interface SqlSource {
  BoundSql getBoundSql(Object parameterObject);
}

SqlSource的实现:

StaticSqlSource:存储解析好的结果

public class StaticSqlSource implements SqlSource {

  private final String sql;  // sql语句
  private final List<ParameterMapping> parameterMappings;  // 参数
  private final Configuration configuration;
}

RawSqlSource:解析普通sql,它会被解析好的结果放到StaticSqlSource中

public class RawSqlSource implements SqlSource {

  private final SqlSource sqlSource;  // 这里实际上就是StaticSqlSource
}
BoundSql

持有Sql语句、参数映射关系parameterMapping、参数。这里的sql语句,设置参数后,就可以在数据库中执行了

public class BoundSql {
  private final String sql;  // sql语句
  private final List<ParameterMapping> parameterMappings;  // 参数映射
  private final Object parameterObject;
  private final Map<String, Object> additionalParameters;
  private final MetaObject metaParameters;
}
statement处理器 StatementHandler

操作jdbc层面的代码,处理Statement实例

1、顶层接口:StatementHandler:

public interface StatementHandler {
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  // 为Statement实例设置参数
  void parameterize(Statement statement)
      throws SQLException;

  /*
   * 执行sql语句的方法
   */
  void batch(Statement statement)
      throws SQLException;

  int update(Statement statement)
      throws SQLException;

  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();
}

2、它的实现类

BaseStatementHandler:基础实现

public abstract class BaseStatementHandler implements StatementHandler {
  
  // 扩展点,初始化statement实例,不同的子类可以创建不同类型的statement实例
  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}

SimpleStatementHandler:处理普通的statement实例

public class SimpleStatementHandler extends BaseStatementHandler {

  // 创建普通的statement实例
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      // 调用jdbc提供的api,创建普通的statement实例
      return connection.createStatement();
    } else {
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

  // 普通的statement实例不需要设置参数
  @Override
  public void parameterize(Statement statement) {
    // N/A
  }

}

PreparedStatementHandler:创建PrepareStatement

public class PreparedStatementHandler extends BaseStatementHandler {

  // 创建PrepareStatement
  @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) {
      // 创建PrepareStatement实例
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

  // 为PrepareStatement设置参数
  @Override
  public void parameterize(Statement statement) throws SQLException {
    // 调用参数处理器,为PrepareStatement实例设置参数
    parameterHandler.setParameters((PreparedStatement) statement);
  }

}
参数处理器 ParameterHandler

操作statement实例,为它设置参数

ParameterHandler:

public interface ParameterHandler {

  Object getParameterObject();

  void setParameters(PreparedStatement ps)
      throws SQLException;

}
返回值解析器 ResultSetHandler

负责解析返回值

ResultSetHandler:

public interface ResultSetHandler {
  // 解析返回值
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;
}
总结

这里介绍了sql执行过程中的组件和它的基本实现,从继承体系来看,mybatis中的继承体系,基本上是 接口 -> 抽象类 -> 具体实现 三层,还有一点,具体实现之间并不是孤立的,很多地方层层代理,组成一个代理链,基于这种模式,如果想要在原有的基础上增加新的功能,不需要继承原来的实现类,只需要创建新的实现类,然后持有原有实现类的实例,就可以了,这个地方比较具有参考价值。

整体流程

大致流程总结:

  • SqlSession调用执行器来执行sql
  • 执行器调用sqlSource,解析sql
  • 执行器调用statementHandler,创建statement实例
  • statementHandler调用参数解析器,为statement实例设置参数
  • statementHandler执行sql
  • statementHandler调用resultSet解析器,解析返回值
SqlSession调用执行器

SqlSession接口中提供了执行sql语句的功能,在这里以selectList为例,mapper接口的代理类就是通过调用selectList等方法来执行sql的。

// 参数1是sql的id
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    // 获取MappedStatement
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 调用执行器来执行sql语句
    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的整体流程
执行器的创建

这里需要先介绍一下执行器是如何创建的,它不是在执行sql的过程中创建的,而是在获取sqlSession实例的时候创建的

功能入口:在DefaultSqlSessionFactory中,创建SqlSession时,需要为SqlSession创建Executor

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

创建执行器:

// Configuration类
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  // 用户配置的执行器类型,默认SIMPLE
  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);   // 重复使用statement实例
  } else {
    executor = new SimpleExecutor(this, transaction);  // 默认的执行器
  }
  // 如果开启了缓存,在普通执行器的基础上,再包一层缓存执行器,缓存执行器在执行完缓存功能后,会调用普通执行器,类似于
  // 代理机制
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // 处理拦截器
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}
执行器的执行

BaseExecutor:以query方法为例

 @Override
 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
   // 1、调用sqlSource将sql解析为可执行的sql
   BoundSql boundSql = ms.getBoundSql(parameter);
   CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
   // 重载的query方法
   return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

调用重载的query方法:

// BaseExecutor
@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++;
    // 一级缓存(SqlSession级别,默认开启)
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
      // 如果一级缓存有数据,使用一级缓存的数据
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
      // 执行sql语句
      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;
}

queryFromDatabase方法:执行sql语句

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方法:真正执行查询功能,这里就是statementHandler、参数处理器、返回值处理器的整体流程

@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实例,并且为它设置参数
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 调用StatementHandler执行查询语句
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

// 创建statement实例
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  // 调用statementHandler,创建statement实例
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 为statement设置参数,这里是statementHandler内部调用参数处理器完成的
  handler.parameterize(stmt);
  return stmt;
}

总结:执行器中做了两件事情,

  • 解析sql,如果sql中包含动态sql,在这里也会负责解析动态sql,生成一个可以执行的sql
  • 调用StatementHandler,执行statement实例
    • 调用参数处理器,为statement实例设置参数
    • 执行statement实例
    • 调用返回值解析器,解析statement实例的返回值
执行器调用SqlSource 解析sql

这里只展示最关键的代码:解析sql的功能入口

public class SqlSourceBuilder extends BaseBuilder {

  // 解析sql
  public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    // 创建解析器,解析sql中的参数,也就是 #{key} 之类的
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql = parser.parse(originalSql);
    // 存储解析结果,sql和它需要的参数
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }
}
执行器调用statement处理器

执行器调用statement处理器来执行sql

1、创建statement处理器:RoutingStatementHandler

public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  // 构造方法。这里其实可以理解为一个工厂方法,用户持有当前类的实例,由它来负责根据statement的类型,寻找合适的处理类。
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }
}

2、使用它来执行sql:这里以PreparedStatementHandler为例,它负责处理PreparedStatement,它支持向sql语句中设置参数,使用设置参数的方式代替字符串拼接,避免sql注入的攻击

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 执行sql,这是jdbc中的代码,之前已经设置好了PreparedStatement,现在可以直接执行了
  ps.execute();
  // 解析返回值
  return resultSetHandler.handleResultSets(ps);
}
调用参数处理器

为sql语句设置参数

public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    // 遍历之前解析好的参数
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        // 参数名
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          // 获取参数的值
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          // 设置参数
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        } catch (SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}
调用返回值解析器

解析返回值:

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

  // 配置文件中配置的resultMap标签
  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);
}

总结

这里介绍的只是整体流程,可以看出,基本是对于jdbc的封装,还有很多细节,包括通过反射操作对象、缓存、插件等,随后介绍