天天看点

JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇

MyBatis学习笔记

  • 源码篇
    • 接口和对象介绍
      • SqlSessionFactoryBuilder
      • XMLConfigBuilder
      • XMLMapperBuilder
      • Configuration
      • SqlSource接口
      • SqlSessionFactory接口
      • SqlSession接口
      • Executor接口
        • BaseExecutor
        • CachingExecutor
      • StatementHandler接口
        • 定义
        • 继承结构
        • prepare方法
        • parameterize方法
        • RoutingStatementHandler
      • ParameterHandler接口
      • ResultSetHandler接口
    • 源码阅读
      • 加载全局配置文件(创建Configuration对象)流程
      • 加载映射文件流程
      • SQL解析流程
      • 获取Mapper代理对象流程
      • SqlSession执行主流程-执行SQL查询流程
      • 设置PreparedStatement的参数流程
      • 结果集映射流程
      • 待分析的问题

源码篇

  • 源码阅读的方法:找主线,找入口,记笔记(类名#方法名(数据成员变量)),参考其他人的源码阅读经验。
  • 源码阅读的目的:通过阅读源码,提升对设计模式的理解,提升编程能力;通过阅读源码,可以找到问题的根源,用来解决问题;应付面试。

接口和对象介绍

SqlSessionFactoryBuilder

JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇

XMLConfigBuilder

  • 专门用来解析全局配置文件的解析器

XMLMapperBuilder

  • 专门用来解析映射文件的解析器

Configuration

  • Mybatis框架支持开发人员通过配置文件与其进行交流,在配置文件中配置的信息,框架运行时,会被XMLConfigBuilder解析并存储在一个Configuration对象中。
  • Configuration对象会被作为参数传给DefaultSqlSessionFactory,而DefaultSqlSessionFactory根据Configuration对象信息为Client创建对应特征的SqlSession对象。

SqlSource接口

JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
  • DynamicSqlSource:主要是封装动态SQL标签解析之后的SQL语句和带有${}的SQL语句
  • RawSqlSource:主要封装带有#{}的SQL语句
  • StaticSqlSource:是BoundSql中要存储SQL语句的一个载体,上面两个SqlSource的SQL语句,最终都会存储到该SqlSource实现类中。

SqlSessionFactory接口

  • 默认实现类是DefaultSqlSessionFactory类

SqlSession接口

  • 默认实现类是DefaultSqlSession类

Executor接口

  • 定义:
public interface Executor {
  ResultHandler NO_RESULT_HANDLER = null;
  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;
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
  List<BatchResult> flushStatements() throws SQLException;
  void commit(boolean required) throws SQLException;
  void rollback(boolean required) throws SQLException;
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  boolean isCached(MappedStatement ms, CacheKey key);
  void clearLocalCache();
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  Transaction getTransaction();
  void close(boolean forceRollback);
  boolean isClosed();
  void setExecutorWrapper(Executor executor);
}
           
  • Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口。
  • 从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。
  • Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建,代码如下:
public Executor newExecutor(Transaction transaction) {
  return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}
           
JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
  • Executor分成两大类:
    • BaseExecutor:抽像类,在创建时会根据传过来的ExecutorType创建不同的类:SimpleExecutor、ReuseExecutor、BatchExecutor。
    • CachingExecutor:先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是 BaseExecutor 中的任一一种。

BaseExecutor

  • SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)
  • ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用(可以是Statement或PrepareStatement对象)。
  • 代码如下:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) {
        stmt = getStatement(sql);
        applyTransactionTimeout(stmt);
    } else {
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
}
private boolean hasStatementFor(String sql) {
    try {
        return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
    } catch (SQLException e) {
        return false;
    }
}
private Statement getStatement(String s) {
    return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
    statementMap.put(sql, stmt);
}
//...
           
  • BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果篮里装了很多苹果,番茄篮里装了很多番茄。最后,再统一倒进仓库。(可以是Statement或PrepareStatement对象)。

CachingExecutor

  • 代码如下:
public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
}
//...
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
                list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}   
...
           

StatementHandler接口

  • 首先约定文中讲的四大对象是指:Executor、StatementHandler、ParameterHandler 和 ResultHandler 接口对象。而StatementHandler 毫无疑问是四大对象中最重要的一个,它的任务就是和数据库对话。在它这里会使用 ParameterHandler 和ResultHandler 对象为我们绑定SQL参数和组装最后的结果返回。

定义

public interface StatementHandler {
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
  void parameterize(Statement statement)
      throws SQLException;
  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();
}
           
  • 这里有几个重要的方法:prepare、parameterize、query、update,他们的作用是不一样的。

继承结构

JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
  • StatementHandler:顶层接口。
  • BaseStatementHandler : 实现顶层接口的抽象类,实现了部分接口,并定义了一个抽象方法。
  • SimpleStatementHandler:对应JDBC中常用的Statement接口,用于简单SQL的处理。
  • PreparedStatementHandler:对应JDBC中的PreparedStatement,预编译SQL的接口。
  • CallableStatementHandler:对应JDBC中CallableStatement,用于执行存储过程相关的接口。
  • RoutingStatementHandler:这个接口是以上三个接口的路由,没有实际操作,只是负责上面三个StatementHandler的创建及调用。
在MyBatis中,Configuration对象会采用new RoutingStatementHandler()来生成StatementHandler对象,换句话说我们真正使用的是RoutingStatementHandler对象,然后它会根据Executor的类型去创建对应具体的statementHandler对象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。然后利用具体statementHandler的方法完成所需要的功能。那么这个具体的statementHandler是保存在RoutingStatementHandler对象的delegate属性的,所以当我们拦截statementHandler的时候就要常常访问它了。

prepare方法

  • BaseStatementHandler的三个子类都继承 prepare() 方法,并没有重写该方法。该方法中调用了一个抽象方法 instantiateStatement()。根据一个 Connection 返回一个Statement 对象;三个子类都实现了该方法,分别返回了 Statement,PrepareStaement 和 CallableStatement 对象。
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    statement = instantiateStatement(connection);
    setStatementTimeout(statement, transactionTimeout);
    setFetchSize(statement);
    return statement;
  } catch (SQLException e) {
    closeStatement(statement);
    throw e;
  } catch (Exception e) {
    closeStatement(statement);
    throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
  }
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
           
  • 显然我们通过源码更加关注抽象方法instantiateStatement是做了什么事情。它依旧是一个抽象方法,那么它就有其实现类。
  • 那就是之前说的那几个具体的StatementHandler对象,让我们看看PreparedStatementHandler:
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  // 获取带有占位符的SQL语句
  String sql = boundSql.getSql();
  if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
    // 处理带有主键返回的SQL
    String[] keyColumnNames = mappedStatement.getKeyColumns();
    if (keyColumnNames == null) {
      return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
    } else {
      return connection.prepareStatement(sql, keyColumnNames);
    }
  } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
    return connection.prepareStatement(sql);
  } else {
    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  }
}
           

这个方法非常简单,我们可以看到它主要是根据上下文来预编译SQL,这时我们还没有设置参数。设置参数的任务是交由statement接口的parameterize方法来实现的。

parameterize方法

  • 进行参数设置:
@Override
public void parameterize(Statement statement) throws SQLException {
// 通过ParameterHandler处理参数
  parameterHandler.setParameters((PreparedStatement) statement);
}
           
  • 抽象类的三个子类都实现了 update()、batch()、query(),在对应的方法中将传入的 Statement 对象转型为 具体的 Statement 类型进行相应的操作。
  • 以PreparedStatementHandler的实现为例:
@Override
public int update(Statement statement) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  int rows = ps.getUpdateCount();
  Object parameterObject = boundSql.getParameterObject();
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  return rows;
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 执行PreparedStatement,也就是执行SQL语句
  ps.execute();
  // 处理结果集
  return resultSetHandler.handleResultSets(ps);
}
           
  • 我们可以看到如果是进行update的,它将会执行生成主键的操作(插入数据要自动生成主键的时候),然后就返回影响行数。
  • 如果是进行query的就更加简单了,它就是执行SQL语句,然后将结果使用resultHandler的handleResultSets去完成我们的结果组装。

RoutingStatementHandler

  • 该类采用了代理模式(静态代理),直接实现了 StatmentHandler 接口,实现了接口的全部方法。该类持有一个 StatmentHandler 的实例,并在创建该类时根据传入的 MappedStatement 的 type 创建不同的 Statement 实例,然后通过这个具体的实例去实现相应的操作。
public class RoutingStatementHandler implements StatementHandler {
  private final StatementHandler delegate;
  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());
    }
  }
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }
  @Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }
  @Override
  public void batch(Statement statement) throws SQLException {
    delegate.batch(statement);
  }
  // 其他方法同理
  ...
}
           

ParameterHandler接口

  • 定义
public interface ParameterHandler {
  Object getParameterObject(); // 获取参数对象
  void setParameters(PreparedStatement ps)
      throws SQLException; // 设置参数对象
}
           
  • 继承结构:只有一个默认的实现类
    JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
  • 默认实现类:DefaultParameterHandler
public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  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 jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
    	    // 给PreparedStatement设置参数
            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);
          }
        }
      }
    }
  }
}
           
  • 它的 setParameters() 只在两处地方被调用,分别为 PreparedStatementHandler 和 CallableStatementHandler 的 parameterize()。

ResultSetHandler接口

  • 定义:
public interface ResultSetHandler {
  // 将Statement执行后产生的结果集(可能有多个结果集)映射为结果列表
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
  // 处理存储过程执行后的输出参数
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}
           
  • 继承结构
    JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
  • 默认实现类:ResultSetHandler的具体实现类是DefaultResultSetHandler,其实现的步骤就是将Statement执行后的结果集,按照Mapper文件中配置的ResultType或ResultMap来封装成对应的对象,最后将封装的对象返回 。

源码阅读

  • Mybatis整体架构
    JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
  • MyBatis源码包对应的架构图
    JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇

加载全局配置文件(创建Configuration对象)流程

  • 找入口:SqlSessionFactoryBuilder#build()
/**
 * 主线一:加载全局配置文件流程
 */
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // XMLConfigBuilder 用来解析XML配置文件
    // 使用构建者模式
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // parser.parse():使用XPath解析XML配置文件,将配置文件信息封装为Configuration对象
    // 返回 DefaultSqlSessionFactory 对象,改对象拥有Configuration对象(封装了配置文件信息的)
    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.
    }
  }
}
           
  • 记笔记
SqlSessionFactoryBuilder#build(...):用于构建SqlSessionFactory对象
	|--XMLConfigBuilder#构造函数(...):用来解析全局文件的解析器
		|--XPathParser#构造函数(...):用来使用XPath语法解析XML的解析器
			|--XPathParser#createDocument(...):解析全局配置文件,封装为Document对象(封装一些子节点,使用XPath语法解析获取)
		|--Configuration#构造函数(...):创建Configuration对象,同时初始化内置类的别名
	|--XMLConfigBuilder#parse(...):全局配置文件的解析器
		|--XPathParser#parse(XPath语法):XPath解析器,专门用来通过XPath语法解析XML返回XNode节点
		|--XMLConfigBuilder#parseConfiguration(XNode):从全局配置文件根节点开始解析,加载的信息设置到Configuration对象中
	|--SqlSessionFactoryBuilder#build(...):创建SqlSessionFactory接口的默认实现类DefaultSqlSessionFactoryBuilder
           
  • 总结:
    • SqlSessionFactoryBuilder创建SqlSessionFactory时,需要传入一个Configuration对象
    • XMLConfigBuilder对象会去实例化Configuration
    • XMLConfigBuilder对象会去初始化Configuration对象
      • 通过XPathParser去解析全局配置文件,形成Document对象
      • 通过XPathParser去获取指定节点的XNode对象
      • 解析XNode对象的信息,然后封装到Configuration对象中
  • 主要涉及到的类:
SqlSessionFactoryBuilder
XMLConfigBuilder
XPathParser
Configuration
           

加载映射文件流程

  • 找入口:XMLConfigBuilder#mapperElement()
/**
 * 主线二:解析<mappers>标签
 */
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 获取<mappers>标签的子标签
    for (XNode child : parent.getChildren()) {
      // <package>子标签
  	if ("package".equals(child.getName())) {
        // 获取mapper接口和mapper映射文件对应的package包名
  	  String mapperPackage = child.getStringAttribute("name");
        // 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中
  	  // key为mapper接口类型,value为代理对象工厂
  	  configuration.addMappers(mapperPackage);
      } else {// <mapper>子标签
        // 获取<mapper>子标签的resource属性
        String resource = child.getStringAttribute("resource");
        // 获取<mapper>子标签的url属性
        String url = child.getStringAttribute("url");
        // 获取<mapper>子标签的class属性
        String mapperClass = child.getStringAttribute("class");
        // 按照 resource --> url --> class 的优先级去解析<mapper>子标签,他们是互斥的
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // XMLMapperBuilder专门用来解析mapper映射文件
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // XMLMapperBuilder调用parse方法解析mapper映射文件
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          // XMLMapperBuilder调用parse方法解析mapper映射文件
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          // 将指定mapper接口以及它的代理对象存储到一个Map集合中
          // key为mapper接口类型,value为代理对象工厂
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}
           
  • 记笔记
XMLConfigBuilder#mapperElement(...):解析全局配置文件的<mappers>标签
	|--XMLMapperBuilder#构造函数(...):专门用来解析映射文件
		|--XPathParser#构造函数(...):
			|--XPathParser#createDocument(...):解析映射文件,封装为Document对象
			|--MapperBuilderAssistant#构造函数(...):用于构建MappedStatement对象
	|--XMLMapperBuilder#parse(...):解析方法
		|--XMLMapperBuilder#configurationElement(...):专门用来解析mapper映射文件
			|--XMLMapperBuilder#buildStatementFromContext(...):用来创建MappedStatement对象的
				|--|--XMLMapperBuilder#buildStatementFromContext(...):
					|--XMLStatementBuilder#构造函数(...):专门用来解析MappedStatement
					|--XMLStatementBuilder#parseStatementNode(...):
						|--MapperBuilderAssistant#addMappedStatement(...):创建MappedStatement
							|--MappedStatement.Builder#构造函数(...)
							|--MappedStatement#build(...):创建MappedStatement对象,并存储到Configuration对象中
           
  • 主要涉及到的类:
XMLConfigBuilder
XMLMapperBuilder
XPathParser
MapperBuilderAssistant
XMLStatementBuilder
MappedStatement.Builder
MappedStatement
           

SQL解析流程

  • 找入口:XMLLanguageDriver#createSqlSource
/**
 * 主线三:SQL解析
 */
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
  // 初始化了动态SQL标签处理器
  XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
  // 解析动态SQL
  return builder.parseScriptNode();
}
           
  • 记笔记
XMLLanguageDriver#createSqlSource(...):获取解析之后的SQL信息,以及参数信息
	|--XMLScriptBuilder#构造函数(...):初始化动态SQL中的节点处理器集合
	|--XMLScriptBuilder#parseScriptNode(...):
		|--XMLScriptBuilder#parseDynamicTags(...):解析动态SQL标签
|--SqlSource#getBoundSql(...)
	|--SqlSourceBuilder#parse(...):将带有#{}的SQL语句进行解析,然后封装到StaticSqlSource中
           
  • 主要涉及到的类:
XMLLanguageDriver
XMLScriptBuilder
SqlSource
SqlSourceBuilder
           

获取Mapper代理对象流程

  • 找入口:DefaultSqlSession#getMapper(…)
/**
 * 主线四:获取Mapper代理对象
 */
@Override
public <T> T getMapper(Class<T> type) {
  // 从Configuration对象中,根据Mapper接口,获取Mapper代理对象
  return configuration.<T>getMapper(type, this);
}
           
  • 记笔记
DefaultSqlSession#getMapper(...):获取Mapper代理对象
	|--Configuration#getMapper(...):获取Mapper代理对象
		|--MapperRegistry#getMapper(...):通过代理对象工厂,获取代理对象
		 |--MapperProxyFactory#newInstance(...):调用JDK的动态代理方式,创建Mapper代理
           
  • 主要涉及到的类:
DefaultSqlSession
Configuration
MapperRegistry
MapperProxyFactory
           

SqlSession执行主流程-执行SQL查询流程

  • 找入口:DefaultSqlSession#selectList(…)
/**
 * 主线五:SqlSession执行
 */
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    // 根据传入的statementId,获取MappedStatement对象
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 调用执行器的查询方法
    // RowBounds是用来逻辑分页(按照条件将数据从数据库查询到内存中,在内存中进行分页)
    // wrapCollection(parameter)是用来装饰集合或者数组参数
    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();
  }
}
           
  • 记笔记
DefaultSqlSession#selectList(...)
	|--CachingExecutor#query(...)
		|--BaseExecutor#query(...)
			|--BaseExecutor#queryFromDatabase(...)
				|--SimpleExecutor#doQuery(...)
					|--Configuration#newStatementHandler(...):创建StatementHandler用来执行MappedStatement对象
						|--RoutingStatementHandler#构造函数(...):根据路由规则,设置不同的StatementHandler
					|--SimpleExecutor#prepareStatement(...):主要设置PreparedStatement的参数
						|--SimpleExecutor#getConnection(...):获取数据库连接
						|--PreparedStatementHandler#prepare(...):创建PreparedStatement对象
						|--PreparedStatementHandler#parameterize:设置PreparedStatement的参数
					|--PreparedStatementHandler#query(...):主要用来执行SQL语句,以及处理结果集
						|--PreparedStatement#execute():调用JDBC的API执行Statement
							|--DefaultResultSetHandler#handleResultSets:处理结果集
						
           
  • 主要涉及到的类:
DefaultSqlSession
CachingExecutor
BaseExecutor
SimpleExecutor
Configuration
RoutingStatementHandler
PreparedStatementHandler
PreparedStatement
DefaultResultSetHandler
           

设置PreparedStatement的参数流程

  • 找入口:PreparedStatementHandler#parameterize(…)
/**
 * 主线六:PreparedStatement 进行参数设置
 */
@Override
public void parameterize(Statement statement) throws SQLException {
// 通过ParameterHandler处理参数
  parameterHandler.setParameters((PreparedStatement) statement);
}
           
  • 记笔记
PreparedStatementHandler#parameterize(...):设置PreparedStatement的参数
	|--DefaultParameterHandler#setParameters(...):设置参数
		|--BaseTypeHandler#setParameter(...):
			|--XxxTypeHandler#setNonNullParameter(...):调用PreparedStatement的setXxx方法
           
  • 主要涉及到的类:
PreparedStatementHandler
DefaultParameterHandler
BaseTypeHandler
XxxTypeHandler
           

结果集映射流程

  • 找入口:DefaultResultSetHandler#handleResultSets(…)
/**
 * 主线七:结果集映射
 */
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

  // <select>标签的resultMap属性,可以指定多个值,多个值之间用逗号(,)分割
  final List<Object> multipleResults = new ArrayList<>();

  int resultSetCount = 0;
  // 这里是获取第一个结果集,将传统JDBC的ResultSet包装成一个包含结果列元信息的ResultSetWrapper对象
  ResultSetWrapper rsw = getFirstResultSet(stmt);

  // 这里是获取所有要映射的ResultMap(按照逗号分割出来的)
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  // 要映射的ResultMap的数量
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  // 循环处理每个ResultMap,从第一个开始处理
  while (rsw != null && resultMapCount > resultSetCount) {
    // 得到结果映射信息
    ResultMap resultMap = resultMaps.get(resultSetCount);
    // 处理结果集
    // 从rsw结果集参数中获取查询结果,再根据resultMap映射信息,将查询结果映射到multipleResults中
    handleResultSet(rsw, resultMap, multipleResults, null);
    
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  // 对应<select>标签的resultSets属性,一般不使用该属性
  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);
}
           
  • 记笔记
DefaultResultSetHandler#handleResultSets(...)
	|--DefaultResultSetHandler#handleResultSet(...)
		|--DefaultResultSetHandler#handleRowValues(...)
			|--DefaultResultSetHandler#handleRowValuesForNestedResultMap(...)
				|--DefaultResultSetHandler#getRowValue(...)
					|--DefaultResultSetHandler#createResultObject(...):创建映射结果对象
					|--DefaultResultSetHandler#applyAutomaticMappings(...)
					|--DefaultResultSetHandler#applyPropertyMappings(...)
           
  • 主要涉及到的类:
DefaultResultSetHandler
           
  • resultType最终会封装成resultMap

待分析的问题

  • 延迟加载的流程
  • 嵌套结果集映射流程

继续阅读