Mybatis源码解读(一)

第一步:读取配置文件

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

将配置文件读成字节数据流,并没有进行解析。

第二步:进行配置文件解析

解析配置文件并封装为一个配置类对象,基于配置类对象并创建一个SqlSessionFactory对象。

sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.1

进一步去源码观察解析代码

 //它使用了一个参照了XML文档或更特定的SqlMapConfig.xml文件的Reader实例。
  //可选的参数是environment和properties。Environment决定加载哪种环境(开发环境/生产环境),包括数据源和事务管理器。
  //如果使用properties,那么就会加载那些properties(属性配置文件),那些属性可以用${propName}语法形式多次用在配置文件中。和Spring很像,一个思想?
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        //委托XMLConfigBuilder来解析xml文件,并构建
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        //parser真正的去执行XMl文件解析。
      return build(parser.parse());
    } catch (Exception e) {
        //这里是捕获异常,包装成自己的异常并抛出的idiom?,最后还要reset ErrorContext
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
2.1.1

我们进入到parser.parse()看一下解析代码。

 //解析配置
  public Configuration parse() {
    //如果已经解析过了,报错,如果没解析过parsed默认为false.
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //开始解析之后进行修改。
    parsed = true;
    //根节点是configuration
    //parser是XPathPaeser解析器对象,读取根节点内的数据  
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
2.1.2

我们先看一下parseConfiguration(XNode root)方法:

private void parseConfiguration(XNode root) {
    try {
      //分步骤解析,对不同标签进行逐步解析。
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.插件
      pluginElement(root.evalNode("plugins"));
      //4.对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.对象包装工厂
                  objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.设置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.环境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
2.1.3

通过以上代码结合注释可知,不同的标签采用不同的方法进行解析,我们就以ropertiesElement(XNode context)为例进行一下追踪。

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:
      //1.在 properties 元素体内指定的属性首先被读取。
      //2.从类路径下资源或 properties 元素的 url 属性中加载的属性第二被读取,它会覆盖已经存在的完全一样的属性。
      //3.作为方法参数传递的属性最后被读取, 它也会覆盖任一已经存在的完全一样的属性,这些属性可能是从 properties 元素体内和资源/url 属性中加载的。
      //传入方式是调用构造函数时传入,public XMLConfigBuilder(Reader reader, String environment, Properties props)
      //1.XNode.getChildrenAsProperties函数方便得到孩子所有Properties
      Properties defaults = context.getChildrenAsProperties();
      //2.然后查找resource或者url,加入前面的Properties
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //3.Variables也全部加入Properties
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      //解析完成之后进行封装,封装到Mybatis的核心配置类当中。  
      configuration.setVariables(defaults);
    }
  }
2.1.4

我们看完了ropertiesElement(XNode context),还有关键的一点,就是2.1.3中的mapperElement(XNode parent),它是对内部的映射文件进行解析。经过以上解析我们就完成了对Configuration对象的封装,我们来看一下这个类。

public class Configuration {

  //环境
  protected Environment environment;

  //---------以下都是<settings>节点-------
  protected boolean safeRowBoundsEnabled = false;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase = false;
  protected boolean aggressiveLazyLoading = true;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys = false;
  protected boolean useColumnLabel = true;
  //默认启用缓存
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls = false;
  
  protected String logPrefix;
  protected Class <? extends Log> logImpl;
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  //默认为简单执行器
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  //---------以上都是<settings>节点-------

  protected Properties variables = new Properties();
  //对象工厂和对象包装器工厂
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  //映射注册机
  protected MapperRegistry mapperRegistry = new MapperRegistry(this);

  //默认禁用延迟加载
  protected boolean lazyLoadingEnabled = false;
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); 
  protected String databaseId;

  protected Class<?> configurationFactory;

  protected final InterceptorChain interceptorChain = new InterceptorChain();
  //类型处理器注册机
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  //类型别名注册机
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  //映射的语句,存在Map里,这里面存放着解析完成的映射文件。
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  //缓存,存在Map里
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  //结果映射,存在Map里
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

  protected final Set<String> loadedResources = new HashSet<String>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

  //不完整的SQL语句
  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
  protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }
    
  public Configuration() {
    //注册更多的类型别名,至于为何不直接在TypeAliasRegistry里注册,还需进一步研究
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
    ...
}

2.2

经过以上分析,我们最终完成了 SqlSessionFactory build(Configuration config)参数的封装。

2.2.1

最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory

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

DefaultSqlSessionFactory就是SqlSessionFactory的实现类,此处使用了构建者设计模式。

第三步:接口代理类的生成流程

SqlSession: SqlSession是一个接口,它有两个实现类DefaultSqlSession和SqlSessionManager(已经弃用)。SqlSession是MyBatis中用于和数据库交互的顶层类,通常将它与ThreadLocal绑定,一个会话使用一个SqlSession,使用完之后进行关闭。

public class DefaultSqlSession implements SqlSession {
  //配置类
  private Configuration configuration;
  //执行器
  private Executor executor;
}

Executor:它是一个执行器,为接口。有三个常用的实现类:BatchExecutor、ReuseExecutor、SimpleExecutor(默认)

介绍完预备知识,进行源码分析。

3.1

sqlSessionFactory.openSession();进行追踪

3.1.1
public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }

第一个参数为执行器类型,默认为:ExecutorType defaultExecutorType = ExecutorType.SIMPLE;,第二个参数为:事务隔离级别,第三个参数为是否自动提交。

3.1.2

来看一下构造方法的具体执行逻辑:

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);
      //然后产生一个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();
    }
  }

3.2

开始执行对应的xml文件内容。交给执行器执行。这里有两种方式,

sqlSession.selectList("namespace.id");
UserDao userDao = sqlSession.getMapper(UserDao.class);

平时工作中第二种用的比较多,我们便以第二种为例子,展开讨论。

3.2.1

我们讨论的这种方式,大致意思就是:通过接口类配合注解(或xml文件)生成一个代理对象。

我们回到解析XML文件代码中进行一下回顾:

InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从上面代码往下追,追到下面代码:
private void parseConfiguration(XNode root) {
    try {
      //分步骤解析,对不同标签进行逐步解析。
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.插件
      pluginElement(root.evalNode("plugins"));
      //4.对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.对象包装工厂
                  objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.设置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.环境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

我们看一下第10步、这步对我们很关键,我们配置的SQL的XML文件就可能在这里面,在MyBatis配置文件中配置的Mapper路径。

我们点进去看一下:

  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //判断你的Mapper里面是 package还是其他 
        if ("package".equals(child.getName())) {
          //如果是package标签并且有name属性,name属性就是XML包的路径。
          String mapperPackage = child.getStringAttribute("name");
          //将这个包名传递到configuration配置类当中去
          //它会将包下的所有接口注册到mapperRegistry[1]中去。
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            //10.1使用类路径
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //映射器比较复杂,调用XMLMapperBuilder
            //注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //10.2使用绝对url路径
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //映射器比较复杂,调用XMLMapperBuilder
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            //10.3使用java类名
            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.");
          }
        }
      }
    }
  }
[1] MapperRegistry

这个对象就是Mapper注册表对象。

public class MapperRegistry {

  private Configuration config;
  //将已经添加的映射都放入HashMap,一个接口对应着一个接口代理工厂。
  //key :接口类型
  //value : 接口代理工厂  
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
}
3.2.2

我们可以开始对UserDao userDao = sqlSession.getMapper(UserDao.class);进行一探究竟了。我们进入到defaultSession中。

@Override
  public <T> T getMapper(Class<T> type) {
    //最后会去调用MapperRegistry.getMapper
    return configuration.<T>getMapper(type, this);
  }

我们继续到configuration.<T>getMapper(type, this);中看一看:

1、
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
2、
 //返回代理类
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      //根据我们的key值从hashMap获取出它的代理工厂类
    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);
    }
  }
3、
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

  protected T newInstance(MapperProxy<T> mapperProxy) {
    //用JDK自带的动态代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

第四步:代理类的执行过程

4.1

经过前面的代码阅读,我们知道了通过JDK动态代理生成了接口代理类,我们去看看具体执行逻辑。

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
    //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //这里优化了,去缓存中找MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行
    return mapperMethod.execute(sqlSession, args);
  }

这段代码是不是很熟悉,Mybatis的插件也是在这块进行的加强。不过我们这次不看上面,看mapperMethod.execute(sqlSession, args);

 //执行
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        //如果有结果处理器
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        //如果结果有多条记录
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        //如果结果是map
        result = executeForMap(sqlSession, args);
      } else {
        //否则就是一条记录
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      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;
  }
4.1.1

根据第三步的SqlSession的默认实现进行分析:其实也类似于第一种情况了:sqlSession.selectList("namespace.id");

我们来到DefaultSqlSession

//核心selectList
//第一个参数1、对应的statementid。2、参数。3、分页
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //根据statement id找到对应的MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      //转而用执行器来查询结果,注意这里传入的ResultHandler是null
      //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();
    }
  }
4.1.2
//我们来到了executor进行执行SQL。
@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //得到绑定sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    //创建缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
4.1.3
  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());
    //如果Executor已经关闭,报错
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    //先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      //加一,这样递归调用到上面的时候就不会再清局部缓存了
      queryStack++;
      //先根据cachekey从localCache去查
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //若查到localCache缓存,处理localOutputParameterCache
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //从数据库查
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      //清空堆栈
      queryStack--;
    }
    if (queryStack == 0) {
      //延迟加载队列中所有元素
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      //清空延迟加载队列
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
    	//如果是STATEMENT,清本地缓存
        clearLocalCache();
      }
    }
    return list;
  }
4.1.4

我们来到list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);这行代码。这行代码为去数据库里进行查询。

 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);
    //如果是存储过程,OUT参数也加入缓存
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
4.1.5

继续追list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

//select-->结果给ResultHandler
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    //先执行Statement.execute,然后交给ResultSetHandler.handleResultSets进行结果集的封装。
    return resultSetHandler.<E>handleResultSets(statement);
  }

总结一句话吧SqlSession开启会话之后,调用executor进行SQL执行,executor又去调用不同的Handler去执行SQL执行的不同阶段

源码贴的有点多,语言解释有点少,解决缺点,早日写出优质博客。后期会逐步修改

posted @ 2021-06-21 15:29  红警贼秀  阅读(69)  评论(0编辑  收藏  举报