Mybatis 执行流程源码层面

一、前言

mybatis 是对 JDBC 的封装,解决了 JDBC 的一些问题

  1. jdbc 每次执行时都要创建一个连接,资源开销大;mybatis 通过连接池解决这个问题
  2. jdbc 的 sql 语句和业务代码强耦合,维护不方便;mybatis 通过 xxxMapper.xml 的方式,将 sql 和业务代码分离
  3. jdbc 的入参通过指定位置和值来实现,很不方便;mybatis 通过 parameterType 参数灵活定义参数
  4. 当我们从 jdbc 的查询结果中获取值时,需要对每一个参数进行类型转换,很不方便,而且当查询语句发生改变时,对应的获取值的代码也要改变;mybatis 通过执行 resultType 来指定结果类型,使用方便。

二、流程概述

  1. 创建SqlSessionFactory对象(加载配置文件)
  2. 创建一个 SqlSession
  3. 通过创建的SqlSession执行 CRUD 语句
  4. 提交 SqlSession#commit
  5. 关闭 SqlSession
		// 1.创建 SqlSessionFactory 
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
		// 2. 创建 SqlSession 
        SqlSession session = sqlSessionFactory.openSession();
        try {
        	// 3. execute
            User user = session.selectOne("xx.mapper.UserMapper.selectById", 1);

            // 创建动态代理
           /* UserMapper mapper = session.getMapper(UserMapper.class);
            System.out.println(mapper.getClass());
            User user = mapper.selectById(1);*/
            System.out.println(user);
            // 4. commit
            session.commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.rollback();
        } finally {
        	// 5. cloes
            session.close();
        }

三、底层实现

1. 创建 SqlSessionFactory 对象

// 1.创建 SqlSessionFactory 
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
1.1 概述

创建 SqlSessionFactory 对象,主要是解析 xml 文件的过程。在解析的过程中首先读取 configuration 根标签,它是解析xml 文件的开始;然后读取 properties 标签扫描属性文件;然后读取 settings、plugins 标签,在这里会根据配置信息进行设置和加载插件;然后读取 environments 标签,environment 标签,在 environment 标签中,会读取 transactionManager 标签,根据 type 属性的别名找到相应的事务管理器,根据 dataSource 标签找到连接池(如果设置了),根据 property 标签找到对应的数据库连接数据;最后读取 mappers 标签下的 mapper 标签,设置 mapper 的地址的方式有四种,分别是package、url、class、name。xml demo 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--properties 扫描属性文件.properties  -->
    <properties resource="db.properties"></properties>
    
    <settings>
    	<!-- 下划线转驼峰命名 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
   <plugins>
       <plugin interceptor="com.xx.plugins.ExamplePlugin" />
    </plugins>
    
    <environments default="development">
        <environment id="development">
           <transactionManager type="JDBC"/>
            <!--//  mybatis内置了JNDI、POOLED、UNPOOLED三种类型的数据源,其中POOLED对应的实现为org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自带实现的一个同步、线程安全的数据库连接池 一般在生产中,我们会使用c3p0或者druid连接池-->
            <dataSource type="POOLED">
            <property name="driver" value="${mysql.driverClass}"/>
            <property name="url" value="${mysql.jdbcUrl}"/>
            <property name="username" value="${mysql.user}"/>
            <property name="password" value="${mysql.password}"/>
        </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--1.必须保证接口名(例如IUserDao)和xml名(IUserDao.xml)相同,还必须在同一个包中-->
        <package name="com.xx.mapper"/>

        <!--2.不用保证同接口同包同名
         <mapper resource="com/mybatis/mappers/EmployeeMapper.xml"/>

        3.保证接口名(例如IUserDao)和xml名(IUserDao.xml)相同,还必须在同一个包中
        <mapper class="com.mybatis.dao.EmployeeMapper"/>

        4.不推荐:引用网路路径或者磁盘路径下的sql映射文件 file:///var/mappers/AuthorMapper.xml
         <mapper url="file:xxx/PersonMapper.xml"/>-->

    </mappers>
</configuration>
1.2 流程概述
1.2.1 首先执行 XMLConfigBuilder#parse,在 parse 中会使用 XPathParser 解析配置文件。
 public Configuration parse() {
    /**
     * 若已经解析过了 就抛出异常
     */
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    /**
     * 设置解析标志位
     */
    parsed = true;
    /**
     * 解析我们的xml的<configuration></configuration>节点
     */
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
1.2.2 parseConfiguration 方法:
 /**
  * root 是 configuration 节点
  */
  private void parseConfiguration(XNode root) {
    try {
      /**
       * 解析 properties节点
       *     <properties resource="mybatis/db.properties" />
       */
      propertiesElement(root.evalNode("properties"));
      /**
       * 解析我们的mybatis-config.xml中的settings节点
       */
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      /**
       * VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源。
       */
      loadCustomVfs(settings);
      /**
       * 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
       * SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
       * 解析到org.apache.ibatis.session.Configuration#logImpl
       */
      loadCustomLogImpl(settings);
      /**
       * 解析我们的别名oorg.apache.ibatis.session.Configuration#typeAliasRegistry.typeAliases
       */
      typeAliasesElement(root.evalNode("typeAliases"));
      /**
       * 解析我们的插件(比如分页插件)org.apache.ibatis.session.Configuration#interceptorChain.interceptors
       */
      pluginElement(root.evalNode("plugins"));

      /**
       * 对象工厂 用于反射实例化对象、对象包装工厂
       */
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));

      // 设置settings 和默认值到configuration
      settingsElement(settings);

      /**
       * 解析我们的mybatis environments 节点
       <environments>
         <environment id="test">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
           <property name="driver" value="${jdbc.driver}"/>
           <property name="url" value="${jdbc.url}"/>
           <property name="username" value="root"/>
           <property name="password" value="123456"/>
           </dataSource>
         </environment>
       </environments>
       *  解析到:org.apache.ibatis.session.Configuration#environment
       *  在集成spring情况下由 spring-mybatis提供数据源和事务工厂
       */
      environmentsElement(root.evalNode("environments"));
      /**
       * 解析数据库厂商
       *     <databaseIdProvider type="DB_VENDOR">
                <property name="SQL Server" value="sqlserver"/>
                <property name="DB2" value="db2"/>
                <property name="Oracle" value="oracle" />
                <property name="MySql" value="mysql" />
             </databaseIdProvider>
       *  解析到:org.apache.ibatis.session.Configuration#databaseId
       */
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /**
       * 解析我们的类型处理器节点
       * <typeHandlers>
            <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
          </typeHandlers>
          解析到:org.apache.ibatis.session.Configuration#typeHandlerRegistry.typeHandlerMap
       */
      typeHandlerElement(root.evalNode("typeHandlers"));
      /**
       * 解析我们的mapper
       *
     	<mappers>
	        <!--1.必须保证接口名(例如IUserDao)和xml名(IUserDao.xml)相同,还必须在同一个包中-->
	        <package name="com.xx.mapper"/>
	
	        <!--2.不用保证同接口同包同名
	         <mapper resource="com/mybatis/mappers/EmployeeMapper.xml"/>
	
	        3.保证接口名(例如IUserDao)和xml名(IUserDao.xml)相同,还必须在同一个包中
	        <mapper class="com.mybatis.dao.EmployeeMapper"/>
	
	        4.不推荐:引用网路路径或者磁盘路径下的sql映射文件 file:///var/mappers/AuthorMapper.xml
	         <mapper url="file:xxx/PersonMapper.xml"/>-->

   		</mappers>
       */
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
1.2.3 解析mappers,mapperElement 方法实现:
/**
* mapper 有四种指定方式:
* 1.通过指定 package
* 2.指定 resource 属性
* 3.指定 url 属性
* 4.指定 name 属性
*/
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      /**
       * 获取我们mappers节点下的一个一个的mapper节点
       */
      for (XNode child : parent.getChildren()) {
        /**
         * 判断我们mapper是不是通过批量注册的
         * <package name="com.tuling.mapper"></package>
         */
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          /**
           * 判断从classpath下读取我们的mapper
           * <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
           */
          String resource = child.getStringAttribute("resource");
          /**
           * 判断是不是从我们的网络资源读取(或者本地磁盘得)
           * <mapper url="D:/mapper/EmployeeMapper.xml"/>
           */
          String url = child.getStringAttribute("url");
          /**
           * 解析这种类型(要求接口和xml在同一个包下)
           * <mapper class="com.xx.mapper.DeptMapper"></mapper>
           *
           */
          String mapperClass = child.getStringAttribute("class");

          /**
           * 我们得mappers节点只配置了
           * <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
           */
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            /**
             * 把我们的文件读取出一个流
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            /**
             * 创建读取XmlMapper构建器对象,用于来解析我们的mapper.xml文件
             */
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            /**
             * 真正的解析我们的mapper.xml配置文件(说白了就是来解析我们的sql)
             */
            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());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            // 将解析后的 mapper 添加到 configure 中
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
1.2.4 将解析后的 mapper 添加到 configure 中
// Configuration#addMappers
public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
}

// MapperRegistry#addMappers
public void addMappers(String packageName, Class<?> superType) {
    // 根据包找到所有类
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    // 循环所有的 mapper
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }
1.2.4.1 addMapper 方法:
public <T> void addMapper(Class<T> type) {
    /**
     * 判断我们传入进来的type类型是不是接口
     */
    if (type.isInterface()) {
      /**
       * 判断我们的缓存中有没有该类型
       */
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        /**
         * 创建一个MapperProxyFactory 把我们的Mapper接口保存到工厂类中, 该工厂用于创建 MapperProxy
         */
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.    mapper注解构造器
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        /**
         * 进行解析, 将接口完整限定名作为xml文件地址去解析
         */
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
1.2.4.2 MapperAnnotationBuilder#parse
public void parse() {
    String resource = type.toString();
    // 是否已经解析mapper接口对应的xml
    if (!configuration.isResourceLoaded(resource)) {
      // 根据mapper接口名获取 xml文件并解析,解析<mapper></mapper>里面所有东西放到configuration
      loadXmlResource();
      // 添加已解析的标记
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      // 获取所有方法 看是不是用了注解
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            // 是不是用了注解  用了注解会将注解解析成MappedStatement
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }
1.2.5 解析完成
  // 配置文件已经解析成了Configuration
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

2. 创建 SqlSession 会话

// 数据源 执行器  DefaultSqlSession 2
SqlSession session = sqlSessionFactory.openSession();
2.1 openSession 方法实现
  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
/**
   * 方法实现说明:从session中开启一个数据源
   * @author:xsls
   * @param execType:执行器类型
   * @param level:隔离级别
   * @return:SqlSession
   * @exception:
   * @date:2019/9/9 13:38
   */
  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);
      /**
       * 创建一个sql执行器对象
       * 一般情况下 若我们的mybaits的全局配置文件的cacheEnabled默认为ture就返回
       * 一个cacheExecutor,若关闭的话返回的就是一个SimpleExecutor
       */
      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();
    }
  }

2.2 获取一个数据源
/**
   * 方法实现说明:从session中开启一个数据源
   * @author:xsls
   * @param execType:执行器类型
   * @param level:隔离级别
   * @return:SqlSession
   * @exception:
   * @date:2019/9/9 13:38
   */
  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);
      /**
       * 创建一个sql执行器对象
       * 一般情况下 若我们的mybaits的全局配置文件的cacheEnabled默认为ture就返回
       * 一个cacheExecutor,若关闭的话返回的就是一个SimpleExecutor
       */
      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();
    }
  }
2.2.1 创建一个sql语句执行器对象
/**
   * 方法实现说明:创建一个sql语句执行器对象
   * @author:xsls
   * @param transaction:事务
   * @param executorType:执行器类型
   * @return:Executor执行器对象
   * @exception:
   * @date:2019/9/9 13:59
   */
  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 {
      //简单的sql执行器对象
      executor = new SimpleExecutor(this, transaction);
    }
    //判断mybatis的全局配置文件是否开启缓存
    if (cacheEnabled) {
      //把当前的简单的执行器包装成一个CachingExecutor
      executor = new CachingExecutor(executor);
    }
    /**
     * TODO:调用所有的拦截器对象plugin方法
     * 插件: 责任链+ 装饰器模式(动态代理)
     */
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
2.2.2 返回一个DefaultSqlSession对象返回
/**
* 创建返回一个DefaultSqlSession对象返回
*/
return new DefaultSqlSession(configuration, executor, autoCommit);

3. 执行 SQL 语句 (底层执行 jdbc)

// 3. execute
User user = session.selectOne("xx.mapper.UserMapper.selectById", 1);

以 SqlSession#selectOne 为例

 <T> T selectOne(String statement, Object parameter);
3.1 DefaultSqlSession#selectOne
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      /**
       * 第一步:通过我们的statement去我们的全局配置类中获取MappedStatement
       *
       * CRUD
       */
      MappedStatement ms = configuration.getMappedStatement(statement);
      /**
       * 通过执行器去执行我们的sql对象
       * 第一步:包装我们的集合类参数
       * 第二步:一般情况下是executor为cacheExetory对象
       */
      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();
    }
  }
3.2 executor#query 方法实现
3.2.1 接口 Executor#query
/**
   * 不走缓存查询
   * @param ms 我们的执行sql包装对象(MappedStatement)
   * @param parameter:参数
   * @param rowBounds 逻辑分页参数
   * @param resultHandler:返回结果处理器
   * @return 结果集list
   * @throws SQLException
   */
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
3.2.2 实现类 CachingExecutor#query
    /**
   * 方法实现说明:通过我们的sql执行器对象执行sql
   * @author:xsls
   * @param ms 用于封装我们一个个的insert|delete|update|select 对象
   * @param parameterObject:参数对象
   * @param rowBounds :mybaits的逻辑分页对象 
   * @param resultHandler:结果处理器对象
   * @return:
   * @exception:
   * @date:2019/9/9 20:39
   */
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    /**
     * 通过参数对象解析我们的sql详细信息1339025938:1570540512:com.xx.mapper.selectById:0:2147483647:select id,user_name,create_time from t_user where id=?:1:development
     */
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 具体执行的 query 方法
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
3.2.2.1 query 实现
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    /**
     * 判断我们我们的mapper中是否开启了二级缓存<cache></cache>
     */
    Cache cache = ms.getCache();
    /**
     * 判断是否配置了<cache></cache>
     */
    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) {
          // 通过查询数据库去查询 delegate: Executor
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //加入到二级缓存中
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //没有整合二级缓存,直接去查询
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
3.2.2.2 底层 query 实现 BaseExecutor#query

通过查询数据库去查询,承接 3.2.2.1 delegate#query

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());
    //已经关闭,则抛出 ExecutorException 异常
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    // <2> 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      // <4.1> 从一级缓存中,获取查询结果
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      // <4.2> 获取到,则进行处理
      if (list != null) {
        //处理存过的
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 获得不到,则从数据库中查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
3.2.2.3 从数据库查询
  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;
  }
3.2.2.4 从数据库中获取的具体实现
  @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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 拿到连接和statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 处理参数
    handler.parameterize(stmt);
    return stmt;
  }
3.2.2.4 handler#query

DefaultResultSetHandler#handleResultSets

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

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

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);//结果集的第一个结果

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);//根据resultMap处理rsw生成java对象
      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);
  }
3.3 plugins 插件 todo

4. commit

5.close

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值