• 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执行的不同阶段

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

  • 相关阅读:
    Linux install
    plafrom library
    lua 线程
    plafrom SDK
    CSS中的focus-within伪类选择器
    网站打开速度优化_如何提高网页访问速度技巧方法总结
    网页预加载_骨架屏Skeleton Screen的实现
    SASS简介及使用方法
    什么是BFC布局——浅析BFC布局的概念以及作用
    JAVA面试题(九):JVM
  • 原文地址:https://www.cnblogs.com/kenai/p/14913641.html
Copyright © 2020-2023  润新知