zoukankan      html  css  js  c++  java
  • 简单读!Mybatis源码(一)一条select的一生

          工具除了会用,还应该多做点。我觉得使用一个软件工具(开源类),一般会经历几个步骤:

    1. 通过wiki了解大致作用,然后开始码代码;

    2. 系统性地学习其特性,找出可能需要的点,用上去;

    3. 通过阅读其源码,清楚其来龙去脉;

    4. 有能力你就去超越别人;

          mybatis作为orm框架给我们带来了很多方便,其定制功能也让我们惊喜!还是来看看别人怎么做到的吧!

      1. 下载git仓库, https://github.com/mybatis/mybatis-3

      2. 打开IDE, 找到 test 包

      3. 进入 org.apache.ibatis.autoconstructor.AutoConstructorTest, 有一个完整的sql 样例

    public class AutoConstructorTest {
      private static SqlSessionFactory sqlSessionFactory;
    
      @BeforeClass
      public static void setUp() throws Exception {
        // create a SqlSessionFactory
        try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
          sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }
    
        // populate in-memory database
        BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
            "org/apache/ibatis/autoconstructor/CreateDB.sql");
      }
    
      @Test
      public void fullyPopulatedSubject() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
          final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
          final Object subject = mapper.getSubject(1);
          assertNotNull(subject);
        }
      }
    
      @Test(expected = PersistenceException.class)
      public void primitiveSubjects() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
          final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
          mapper.getSubjects();
        }
      }
    
      @Test
      public void annotatedSubject() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
          final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
          verifySubjects(mapper.getAnnotatedSubjects());
        }
      }
    
      @Test(expected = PersistenceException.class)
      public void badSubject() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
          final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
          mapper.getBadSubjects();
        }
      }
    
      @Test
      public void extensiveSubject() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
          final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
          verifySubjects(mapper.getExtensiveSubject());
        }
      }
    
      private void verifySubjects(final List<?> subjects) {
        assertNotNull(subjects);
        Assertions.assertThat(subjects.size()).isEqualTo(3);
      }
    }

     目标方法:fullyPopulatedSubject(),最终进行一条 select 查询:

    SELECT * FROM subject WHERE id = #{id}

    首先执行的肯定 setup()方法了,setup()做了三件事:

        1. 加载mybatis-config文件读取到reader中;

        2. 将配置文件传递工厂builder中创建会话工厂;

        3. 执行hsqldb内存数据库的初始化工作;(非本文重点)

    接下来,咱们从源码的角度,主要看会话工厂的创建过程!

    创建 sqlSessionFactory

       try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
          sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }
        
    // SqlSessionFactoryBuilder.build(reader) -> XMLConfigBuilder.parse()
    // org.apache.ibatis.session.SqlSessionFactoryBuilder
      public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
      }
      // 建立factory
      public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            // 加载完成后,上下文管理器重置
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }

     标签解析:

    /**
     * Offline entity resolver for the MyBatis DTDs
     * 
     * @author Clinton Begin
     * @author Eduardo Macarron
     */
    public class XMLMapperEntityResolver implements EntityResolver {
    
      private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
      private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
      private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
      private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
    
      private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
      private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
    
      /**
       * Converts a public DTD into a local one
       * 
       * @param publicId The public id that is what comes after "PUBLIC"
       * @param systemId The system id that is what comes after the public id.
       * @return The InputSource for the DTD
       * 
       * @throws org.xml.sax.SAXException If anything goes wrong
       */
      @Override
      public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
        try {
          if (systemId != null) {
            String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
            if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
              return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
            } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
              return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
            }
          }
          return null;
        } catch (Exception e) {
          throw new SAXException(e.toString());
        }
      }
    
      private InputSource getInputSource(String path, String publicId, String systemId) {
        InputSource source = null;
        if (path != null) {
          try {
            InputStream in = Resources.getResourceAsStream(path);
            source = new InputSource(in);
            source.setPublicId(publicId);
            source.setSystemId(systemId);        
          } catch (IOException e) {
            // ignore, null is ok
          }
        }
        return source;
      }
    
    }

     parser

    // org.apache.ibatis.parsing.XPathParser.XPathParser(), 设备一些必要参数
    
      public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
        commonConstructor(validation, variables, entityResolver);
        this.document = createDocument(new InputSource(reader));
      }
      private Document createDocument(InputSource inputSource) {
        // important: this must only be called AFTER common constructor
        try {
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          factory.setValidating(validation);
    
          factory.setNamespaceAware(false);
          factory.setIgnoringComments(true);
          factory.setIgnoringElementContentWhitespace(false);
          factory.setCoalescing(false);
          factory.setExpandEntityReferences(true);
    
          DocumentBuilder builder = factory.newDocumentBuilder();
          builder.setEntityResolver(entityResolver);
          builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void error(SAXParseException exception) throws SAXException {
              throw exception;
            }
    
            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
              throw exception;
            }
    
            @Override
            public void warning(SAXParseException exception) throws SAXException {
            }
          });
          return builder.parse(inputSource);
        } catch (Exception e) {
          throw new BuilderException("Error creating document instance.  Cause: " + e, e);
        }
      }
      


    // 在返回fatory的时候调用 return build(parser.parse()); 将 config.xml 的配置全部解析到 configuration 实例中

    // 在返回fatory的时候调用 return build(parser.parse());  将 config.xml 的配置全部解析到 configuration 实例中
    // org.apache.ibatis.builder.xml.XMLConfigBuilder.parse()
      public Configuration parse() {
        if (parsed) {
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        // 根元素为 /configuration, 依次向下解析
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
      }
      
      // org.apache.ibatis.parsing.XPathParser.evalNode() 映射 xml 属性到 bean 
      public XNode evalNode(Object root, String expression) {
        Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
        if (node == null) {
          return null;
        }
        return new XNode(this, node, variables);
      }
    
      private Object evaluate(String expression, Object root, QName returnType) {
        try {
          return xpath.evaluate(expression, root, returnType);
        } catch (Exception e) {
          throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
        }
      }
      

    具体解析项如下,从这里也一目了然,配置项支持什么:

      // 解析各配置参数到实例中
      private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          propertiesElement(root.evalNode("properties"));
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          // 这个配置没有用过吧
          loadCustomVfs(settings);
          // 解析别名设置
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          // 重要节点,解析 mapper
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }

    下面依次来看一下都怎么解析各配置项的吧~

    properties 解析

      // propertiesElement(root.evalNode("properties"));  url, resource 解析
      private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
          Properties defaults = context.getChildrenAsProperties();
          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));
          }
          Properties vars = configuration.getVariables();
          if (vars != null) {
            defaults.putAll(vars);
          }
          // 解析完成后,放到 parser 和 configuration 实例中
          parser.setVariables(defaults);
          configuration.setVariables(defaults);
        }
      }

    settings 配置项解析,返回内容待处理

      // Properties settings = settingsAsProperties(root.evalNode("settings"));
      private Properties settingsAsProperties(XNode context) {
        if (context == null) {
          return new Properties();
        }
        Properties props = context.getChildrenAsProperties();
        // Check that all settings are known to the configuration class
        MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
        for (Object key : props.keySet()) {
          if (!metaConfig.hasSetter(String.valueOf(key))) {
            throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
          }
        }
        return props;
      }
      
      // loadCustomVfs(settings);  这个...
      private void loadCustomVfs(Properties props) throws ClassNotFoundException {
        String value = props.getProperty("vfsImpl");
        if (value != null) {
          String[] clazzes = value.split(",");
          for (String clazz : clazzes) {
            if (!clazz.isEmpty()) {
              @SuppressWarnings("unchecked")
              Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
              configuration.setVfsImpl(vfsImpl);
            }
          }
        }
      }
      

    typeAliases 别名设置解析,主要做类型检查,及别名的注册工作

      // typeAliasesElement(root.evalNode("typeAliases"));
      private void typeAliasesElement(XNode parent) {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
              // 针对package配置,需注册一系列别名,以 simpleName 代替
              String typeAliasPackage = child.getStringAttribute("name");
              configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
            } else {
              String alias = child.getStringAttribute("alias");
              String type = child.getStringAttribute("type");
              try {
                Class<?> clazz = Resources.classForName(type);
                if (alias == null) {
                  typeAliasRegistry.registerAlias(clazz);
                } else {
                  typeAliasRegistry.registerAlias(alias, clazz);
                }
              } catch (ClassNotFoundException e) {
                throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
              }
            }
          }
        }
      }
      // org.apache.ibatis.type.TypeAliasRegistry.registerAlias(), 具体数据结构为 map 封装
      public void registerAlias(Class<?> type) {
        String alias = type.getSimpleName();
        Alias aliasAnnotation = type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
          alias = aliasAnnotation.value();
        } 
        registerAlias(alias, type);
      }
      public void registerAlias(String alias, Class<?> value) {
        if (alias == null) {
          throw new TypeException("The parameter alias cannot be null");
        }
        // issue #748
        String key = alias.toLowerCase(Locale.ENGLISH);
        if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
          throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
        }
        TYPE_ALIASES.put(key, value);
      }

    plugins 配置项解析,将属性注入的plugin, 添加 plugin 到 configuration

      // pluginElement(root.evalNode("plugins"));
      private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            String interceptor = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            // 先创建一个plugin实例,备用
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            // 配置属性的注入,回调
            interceptorInstance.setProperties(properties);
            // 添加到 configuration
            configuration.addInterceptor(interceptorInstance);
          }
        }
      }
    public class InterceptorChain {
    
      private final List<Interceptor> interceptors = new ArrayList<>();
    
      public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
          target = interceptor.plugin(target);
        }
        return target;
      }
    
      public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
      }
      
      public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
      }
    
    }

    objectFactory 配置项解析,与plugin原理相似,做替换自定义作用

      // objectFactoryElement(root.evalNode("objectFactory"));
      private void objectFactoryElement(XNode context) throws Exception {
        if (context != null) {
          String type = context.getStringAttribute("type");
          Properties properties = context.getChildrenAsProperties();
          ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
          factory.setProperties(properties);
          configuration.setObjectFactory(factory);
        }
      }
      
      // objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 没什么特别
      private void objectWrapperFactoryElement(XNode context) throws Exception {
        if (context != null) {
          String type = context.getStringAttribute("type");
          ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
          configuration.setObjectWrapperFactory(factory);
        }
      }
      
      // reflectorFactoryElement(root.evalNode("reflectorFactory"));
      private void reflectorFactoryElement(XNode context) throws Exception {
        if (context != null) {
           String type = context.getStringAttribute("type");
           ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
           configuration.setReflectorFactory(factory);
        }
      }

    settings 选项解析,设置各种开关, 如缓存

      // settingsElement(settings);  设置各种开关, 如缓存
      private void settingsElement(Properties props) throws Exception {
        configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
        configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
        configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
        configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
        configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
        configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
        configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
        configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
        configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
        configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
        configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
        configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
        configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
        configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
        configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
        configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
        configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
        configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
        configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
        @SuppressWarnings("unchecked")
        Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));
        configuration.setDefaultEnumTypeHandler(typeHandler);
        configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
        configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
        configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
        configuration.setLogPrefix(props.getProperty("logPrefix"));
        @SuppressWarnings("unchecked")
        Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));
        configuration.setLogImpl(logImpl);
        configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
      }
      

     environments 环境配置设置,根据指定环境,获取相应配置,加载数据源配置

      // environmentsElement(root.evalNode("environments")); 环境配置
      private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
          if (environment == null) {
            // 默认为 development
            environment = context.getStringAttribute("default");
          }
          for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            // 查找当前环境配置,指定加载
            if (isSpecifiedEnvironment(id)) {
              // 事务管理、数据源
              TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
              DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
              DataSource dataSource = dsFactory.getDataSource();
              Environment.Builder environmentBuilder = new Environment.Builder(id)
                  .transactionFactory(txFactory)
                  .dataSource(dataSource);
              configuration.setEnvironment(environmentBuilder.build());
            }
          }
        }
      }
        // 环境配置包含, id, 事务管理, 数据源
        public Environment build() {
          return new Environment(this.id, this.transactionFactory, this.dataSource);
        }

     databaseIdProvider 解析,对多数据源,包含多 机器,或多类型数据库 mysql,sqlserver

      // databaseIdProviderElement(root.evalNode("databaseIdProvider")); 
      private void databaseIdProviderElement(XNode context) throws Exception {
        DatabaseIdProvider databaseIdProvider = null;
        if (context != null) {
          String type = context.getStringAttribute("type");
          // awful patch to keep backward compatibility
          if ("VENDOR".equals(type)) {
              type = "DB_VENDOR";
          }
          Properties properties = context.getChildrenAsProperties();
          databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
          databaseIdProvider.setProperties(properties);
        }
        Environment environment = configuration.getEnvironment();
        if (environment != null && databaseIdProvider != null) {
          String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
          configuration.setDatabaseId(databaseId);
        }
      }

    自定义 typeHandler

      // typeHandlerElement(root.evalNode("typeHandlers")); 自定义 typeHandler
      private void typeHandlerElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
              String typeHandlerPackage = child.getStringAttribute("name");
              typeHandlerRegistry.register(typeHandlerPackage);
            } else {
              String javaTypeName = child.getStringAttribute("javaType");
              String jdbcTypeName = child.getStringAttribute("jdbcType");
              String handlerTypeName = child.getStringAttribute("handler");
              Class<?> javaTypeClass = resolveClass(javaTypeName);
              JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
              Class<?> typeHandlerClass = resolveClass(handlerTypeName);
              if (javaTypeClass != null) {
                if (jdbcType == null) {
                  typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
                } else {
                  typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
                }
              } else {
                typeHandlerRegistry.register(typeHandlerClass);
              }
            }
          }
        }
      }
      // org.apache.ibatis.type.TypeHandlerRegistry.register(), 以map保存映射
      private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
        if (javaType != null) {
          Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
          if (map == null || map == NULL_TYPE_HANDLER_MAP) {
            map = new HashMap<>();
            TYPE_HANDLER_MAP.put(javaType, map);
          }
          map.put(jdbcType, handler);
        }
        ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
      }

     mapper 解析,crud

      // mapperElement(root.evalNode("mappers"));
      private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
              String mapperPackage = child.getStringAttribute("name");
              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) {
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                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);
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }

    mapper解析细节

      // org.apache.ibatis.builder.xml.XMLMapperBuilder.parse() mapper解析细节
      public void parse() {
        // 只解析一次 namespace
        if (!configuration.isResourceLoaded(resource)) {
          configurationElement(parser.evalNode("/mapper"));
          configuration.addLoadedResource(resource);
          bindMapperForNamespace();
        }
    
        // 解析 resultMaps
        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
      }
      private void configurationElement(XNode context) {
        try {
          String namespace = context.getStringAttribute("namespace");
          if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          builderAssistant.setCurrentNamespace(namespace);
          cacheRefElement(context.evalNode("cache-ref"));
          cacheElement(context.evalNode("cache"));
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          sqlElement(context.evalNodes("/mapper/sql"));
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
      }
    
      private void parsePendingResultMaps() {
        Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
        synchronized (incompleteResultMaps) {
          Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
          while (iter.hasNext()) {
            try {
              iter.next().resolve();
              iter.remove();
            } catch (IncompleteElementException e) {
              // ResultMap is still missing a resource...
            }
          }
        }
      }
    
      private void parsePendingCacheRefs() {
        Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
        synchronized (incompleteCacheRefs) {
          Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
          while (iter.hasNext()) {
            try {
              iter.next().resolveCacheRef();
              iter.remove();
            } catch (IncompleteElementException e) {
              // Cache ref is still missing a resource...
            }
          }
        }
      }
    
      private void parsePendingStatements() {
        Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
        synchronized (incompleteStatements) {
          Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
          while (iter.hasNext()) {
            try {
              iter.next().parseStatementNode();
              iter.remove();
            } catch (IncompleteElementException e) {
              // Statement is still missing a resource...
            }
          }
        }
      }
    View Code

    // 最后,返回一个 DefaultSqlSessionFactory, 载入配置项

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

    // 会话创建好后,可以执行sql了

      public void fullyPopulatedSubject() {
        // 打开一个连接,使用完成后关闭
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
          final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
          final Object subject = mapper.getSubject(1);
          assertNotNull(subject);
        }
      }

    2. 打开获得一个数据库会话连接

      
    // org.apache.ibatis.session.defaults.SqlSession.openSession(); 打开一个数据库连接
    
      @Override
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
      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);
          // new Executor
          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();
        }
      }
      // org.apache.ibatis.session.Configuration.newExecutor()
      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        // 根据不同请求类型创建不同的 executor
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          // 默认为 SIMPLE
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          // 创建缓存管理,将缓存请求委托给 executor 处理
          executor = new CachingExecutor(executor);
        }
        // 处理plugin 请求
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
      // org.apache.ibatis.executor.CachingExecutor
      public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
      }
      
      // 最后将加载好的各种配置和实际处理,由 DefaultSqlSession 包装好后返回
      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }

    3. 获取mapper,准备调用查询

      // org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper()
      @Override
      public <T> T getMapper(Class<T> type) {
        return configuration.<T>getMapper(type, this);
      }
      
      // org.apache.ibatis.binding.MapperRegistry.getMapper()
      @SuppressWarnings("unchecked")
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // 获取 MapperProxyFactory
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
          // 创建 mapper 实例 
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
    
      // org.apache.ibatis.binding.newInstance(); 
      public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }

    4. select, 查询数据

      //mapper.getSubject(1); 查询数据
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        // 加载缓存包装方法
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 执行查询
        return mapperMethod.execute(sqlSession, args);
      }
      
      private MapperMethod cachedMapperMethod(Method method) {
        return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
    
      // org.apache.ibatis.binding.MapperMethod.execute(sqlSession, args); 针对不同类型语句,做不同调用
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
          case INSERT: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
          }
          case UPDATE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
          }
          case DELETE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
          }
          case SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {
              executeWithResultHandler(sqlSession, args);
              result = null;
            } else if (method.returnsMany()) {
              result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
              result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {
              result = executeForCursor(sqlSession, args);
            } else {
              // 转换参数
              Object param = method.convertArgsToSqlCommandParam(args);
              // 执行单条查询
              result = sqlSession.selectOne(command.getName(), param);
              if (method.returnsOptional() &&
                  (result == null || !method.getReturnType().equals(result.getClass()))) {
                result = Optional.ofNullable(result);
              }
            }
            break;
          case FLUSH:
            result = sqlSession.flushStatements();
            break;
          default:
            throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
          throw new BindingException("Mapper method '" + command.getName() 
              + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        return result;
      }
      // org.apache.ibatis.reflection.ParamNameResolver.getNamedParams() 返回参数,如果是单个参数,直接返回参数值
      //  如果是多个参数,则以 map 形式返回
      public Object getNamedParams(Object[] args) {
        final int paramCount = names.size();
        if (args == null || paramCount == 0) {
          return null;
        } else if (!hasParamAnnotation && paramCount == 1) {
          return args[names.firstKey()];
        } else {
          final Map<String, Object> param = new ParamMap<>();
          int i = 0;
          for (Map.Entry<Integer, String> entry : names.entrySet()) {
            param.put(entry.getValue(), args[entry.getKey()]);
            // add generic param names (param1, param2, ...)
            final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
            // ensure not to overwrite parameter named with @Param
            if (!names.containsValue(genericParamName)) {
              param.put(genericParamName, args[entry.getKey()]);
            }
            i++;
          }
          return param;
        }
      }
    View Code

    selectOne

      // org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(command.getName(), param);
      @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        // 查询单条记录时,默认也是先查询list,然后取第一条
        List<T> list = this.<T>selectList(statement, parameter);
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }
      
      // 查询列表
      @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          // 先获取执行语句,此处 statement 为 id
          MappedStatement ms = configuration.getMappedStatement(statement);
          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();
        }
      }
      
      // org.apache.ibatis.session.Configuration.getMappedStatement(id), 获取初始化解析出来的语句
      public MappedStatement getMappedStatement(String id) {
        return this.getMappedStatement(id, true);
      }
      public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
        if (validateIncompleteStatements) {
          buildAllStatements();
        }
        return mappedStatements.get(id);
      }

    包装执行 query

      // executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      // org.apache.ibatis.executor.CachingExecutor.query()
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 获取sql
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
      
      // org.apache.ibatis.mapping.MappedStatement.getBoundSql()
      public BoundSql getBoundSql(Object parameterObject) {
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        // 字段映射
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings == null || parameterMappings.isEmpty()) {
          boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
        }
    
        // check for nested result maps in parameter mappings (issue #30)
        for (ParameterMapping pm : boundSql.getParameterMappings()) {
          String rmId = pm.getResultMapId();
          if (rmId != null) {
            ResultMap rm = configuration.getResultMap(rmId);
            if (rm != null) {
              hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
          }
        }
    
        return boundSql;
      }
      
      // CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
      // return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      // org.apache.ibatis.executor.CachingExecutor.query()
      @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);
      }

    前面的query是一个cache的包装类,其实际查询动作需要委托给excuttor,而我们主要关心 queryFromDb 逻辑!

      // org.apache.ibatis.executor.SimpleExecutor.query() / BaseExecutor
      @SuppressWarnings("unchecked")
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        // 缓存刷新
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            // 没有缓存,走db
            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;
      }
    
      // BaseExecutor
      private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        // 缓存占位符
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          // 防止异常情况,保证将占位缓存删除
          localCache.removeObject(key);
        }
        // 操作完成后,重新放入缓存
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
          localOutputParameterCache.putObject(key, parameter);
        }
        return list;
      }
      

    执行 doQuery,主要是获取完整的 statement,使handler有据可查!

      @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();
          // 获取statement, 处理 JDBC 接口
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
        // org.apache.ibatis.session.Configuration.newStatementHandler(), 兼顾 plugins, 
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }

    db查询

      // 为不同处理类型,建立不同的 handler
      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());
        }
    
      }
      
      //stmt = prepareStatement(handler, ms.getStatementLog());
      // org.apache.ibatis.executor.SimpleExecutor.prepareStatement(), 获取 statement, JDBC
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        // sql录入
        stmt = handler.prepare(connection, transaction.getTimeout());
        // 参数绑定
        handler.parameterize(stmt);
        return stmt;
      }
      
      // BaseExecutor
      protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = transaction.getConnection();
        if (statementLog.isDebugEnabled()) {
          return ConnectionLogger.newInstance(connection, statementLog, queryStack);
        } else {
          return connection;
        }
      }
    
      // org.apache.ibatis.transaction.jdbc.JdbcTransaction.getConnection()
      @Override
      public Connection getConnection() throws SQLException {
        if (connection == null) {
          // 为空时新建 connection 
          openConnection();
        }
        return connection;
      }
    
      
      protected void openConnection() throws SQLException {
        if (log.isDebugEnabled()) {
          log.debug("Opening JDBC Connection");
        }
        connection = dataSource.getConnection();
        if (level != null) {
          connection.setTransactionIsolation(level.getLevel());
        }
        setDesiredAutoCommit(autoCommit);
      }
      
      // org.apache.ibatis.datasource.unpooled.getConnection()
      @Override
      public Connection getConnection() throws SQLException {
        return doGetConnection(username, password);
      }
    
      private Connection doGetConnection(String username, String password) throws SQLException {
        Properties props = new Properties();
        if (driverProperties != null) {
          props.putAll(driverProperties);
        }
        if (username != null) {
          props.setProperty("user", username);
        }
        if (password != null) {
          props.setProperty("password", password);
        }
        return doGetConnection(props);
      }
      
      private Connection doGetConnection(Properties properties) throws SQLException {
        // 如果没有初始化驱动,初始化
        initializeDriver();
        // 
        Connection connection = DriverManager.getConnection(url, properties);
        configureConnection(connection);
        return connection;
      }
    
      private synchronized void initializeDriver() throws SQLException {
        if (!registeredDrivers.containsKey(driver)) {
          Class<?> driverType;
          try {
            // 加载驱动
            if (driverClassLoader != null) {
              driverType = Class.forName(driver, true, driverClassLoader);
            } else {
              driverType = Resources.classForName(driver);
            }
            // DriverManager requires the driver to be loaded via the system ClassLoader.
            // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
            Driver driverInstance = (Driver)driverType.newInstance();
            DriverManager.registerDriver(new DriverProxy(driverInstance));
            registeredDrivers.put(driver, driverInstance);
          } catch (Exception e) {
            throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
          }
        }
      }
      
      private void configureConnection(Connection conn) throws SQLException {
        if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
          conn.setAutoCommit(autoCommit);
        }
        if (defaultTransactionIsolationLevel != null) {
          conn.setTransactionIsolation(defaultTransactionIsolationLevel);
        }
      }
      
      
    // stmt = handler.prepare(connection, transaction.getTimeout()); 组装sql
    // org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare()
      @Override
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        return delegate.prepare(connection, transactionTimeout);
      }
      
      // org.apache.ibatis.executor.statement.PreparedStatementHandler.  
      @Override
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
          // select * from tab where id = ?
          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);
        }
      }
    
    
      // 调用jdbc connection.prepareStatement(sql), 初始化语句
      @Override
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
          String[] keyColumnNames = mappedStatement.getKeyColumns();
          if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
          } else {
            return connection.prepareStatement(sql, keyColumnNames);
          }
        } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
          return connection.prepareStatement(sql);
        } else {
          return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
      }
      
      // PreparedStatementHandler handler.parameterize(stmt); 
      @Override
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    
      // org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters()
      @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 typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) {
                jdbcType = configuration.getJdbcTypeForNull();
              }
              try {
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
              } catch (TypeException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              } catch (SQLException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              }
            }
          }
        }
      }
      
      // return handler.<E>query(stmt, resultHandler);
      // org.apache.ibatis.executor.statement.RoutingStatementHandler.query()
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.<E>query(statement, resultHandler);
      }
      
      
      // PreparedStatementHandler.query()
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 调用驱动程序执行 execute
        ps.execute();
        // 处理结果集映射
        return resultSetHandler.<E> handleResultSets(ps);
      }
    
      // org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(), 处理结果集映射
      @Override
      public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    
        final List<Object> multipleResults = new ArrayList<>();
    
        int resultSetCount = 0;
        // 获取第一个结果集(开始偏移的地方)
        ResultSetWrapper rsw = getFirstResultSet(stmt);
    
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        while (rsw != null && resultMapCount > resultSetCount) {
          ResultMap resultMap = resultMaps.get(resultSetCount);
          handleResultSet(rsw, resultMap, multipleResults, null);
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
        }
    
        String[] resultSets = mappedStatement.getResultSets();
        if (resultSets != null) {
          while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
              String nestedResultMapId = parentMapping.getNestedResultMapId();
              ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
              handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
        }
    
        return collapseSingleResultList(multipleResults);
      }
      
      // org.apache.ibatis.executor.resultset.ResultSetWrapper, 根据第一行返回数据集,初始化字段名及字段类型
      public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
        super();
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.resultSet = rs;
        final ResultSetMetaData metaData = rs.getMetaData();
        final int columnCount = metaData.getColumnCount();
        for (int i = 1; i <= columnCount; i++) {
          columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
          // java.sql.Types, 处理返回的类型为具体的java类型映射到 org.apache.ibatis.type.JdbcType
          jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
          // java 类型全名
          classNames.add(metaData.getColumnClassName(i));
        }
      }
    View Code

    数据库结果映射到javabean

      // DefaultResultSetHandler.handleResultSet() 处理单条记录映射关系
      private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
          if (parentMapping != null) {
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
          } else {
            if (resultHandler == null) {
              DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
              handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
              multipleResults.add(defaultResultHandler.getResultList());
            } else {
              handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
          }
        } finally {
          // issue #228 (close resultsets)
          closeResultSet(rsw.getResultSet());
        }
      }
      // handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
      public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {
          // 嵌套数据的映射
          ensureNoRowBounds();
          checkResultHandler();
          handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
          // 简单数据映射
          handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
      }
      // 简单数据映射
      private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
          throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
        ResultSet resultSet = rsw.getResultSet();
        // 跳过行偏移,难道不是在数据sql中添加 limit 进行数据筛选的?
        skipRows(resultSet, rowBounds);
        while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
          // 1. Discriminated 鉴别器
          ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
          // 
          Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
          storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
      }
      // 检测超过 limit 后,不再继续获取结果
      private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
        return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
      }
      // Discriminated
      public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
        Set<String> pastDiscriminators = new HashSet<>();
        Discriminator discriminator = resultMap.getDiscriminator();
        while (discriminator != null) {
          final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
          final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
          if (configuration.hasResultMap(discriminatedMapId)) {
            resultMap = configuration.getResultMap(discriminatedMapId);
            Discriminator lastDiscriminator = discriminator;
            discriminator = resultMap.getDiscriminator();
            if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
              break;
            }
          } else {
            break;
          }
        }
        return resultMap;
      }
        
      // 从结果set中获取值
      private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
          final MetaObject metaObject = configuration.newMetaObject(rowValue);
          boolean foundValues = this.useConstructorMappings;
          if (shouldApplyAutomaticMappings(resultMap, false)) {
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
          }
          foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
          foundValues = lazyLoader.size() > 0 || foundValues;
          rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
      }
      
      // DefaultResultSetHandler
      private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
          throws SQLException {
        final Class<?> resultType = resultMap.getType();
        final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
        final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (hasTypeHandlerForResultObject(rsw, resultType)) {
          return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (!constructorMappings.isEmpty()) {
          return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
          return objectFactory.create(resultType);
        } else if (shouldApplyAutomaticMappings(resultMap, false)) {
          return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
        }
        throw new ExecutorException("Do not know how to create an instance of " + resultType);
      }
      
      // 构造器注入方式
      private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
                                                  String columnPrefix) throws SQLException {
        final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
        final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
        if (defaultConstructor != null) {
          return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, defaultConstructor);
        } else {
          for (Constructor<?> constructor : constructors) {
            if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
              return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, constructor);
            }
          }
        }
        throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
      }
      
      private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) throws SQLException {
        boolean foundValues = false;
        for (int i = 0; i < constructor.getParameterTypes().length; i++) {
          Class<?> parameterType = constructor.getParameterTypes()[i];
          String columnName = rsw.getColumnNames().get(i);
          TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
          Object value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(columnName, columnPrefix));
          constructorArgTypes.add(parameterType);
          constructorArgs.add(value);
          foundValues = value != null || foundValues;
        }
        // 调用构造方法注入值
        return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
      }
      // org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(), 调用构造器返回 bean 实例
      @SuppressWarnings("unchecked")
      @Override
      public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        Class<?> classToCreate = resolveInterface(type);
        // we know types are assignable
        return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
      }
    
      private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        try {
          Constructor<T> constructor;
          if (constructorArgTypes == null || constructorArgs == null) {
            constructor = type.getDeclaredConstructor();
            try {
              return constructor.newInstance();
            } catch (IllegalAccessException e) {
              if (Reflector.canControlMemberAccessible()) {
                constructor.setAccessible(true);
                return constructor.newInstance();
              } else {
                throw e;
              }
            }
          }
          constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
          try {
            return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
          } catch (IllegalAccessException e) {
            if (Reflector.canControlMemberAccessible()) {
              constructor.setAccessible(true);
              return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
            } else {
              throw e;
            }
          }
        } catch (Exception e) {
          StringBuilder argTypes = new StringBuilder();
          if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
            for (Class<?> argType : constructorArgTypes) {
              argTypes.append(argType.getSimpleName());
              argTypes.append(",");
            }
            argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
          }
          StringBuilder argValues = new StringBuilder();
          if (constructorArgs != null && !constructorArgs.isEmpty()) {
            for (Object argValue : constructorArgs) {
              argValues.append(String.valueOf(argValue));
              argValues.append(",");
            }
            argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
          }
          throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
        }
      }
    View Code

    数据集获取

      // 保存结果
      private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        if (parentMapping != null) {
          linkToParents(rs, parentMapping, rowValue);
        } else {
          callResultHandler(resultHandler, resultContext, rowValue);
        }
      }
    
      @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
      private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
        resultContext.nextResultObject(rowValue);
        ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
      }
      // 
      private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
          String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
          if (propertyMapping.getNestedResultMapId() != null) {
            // the user added a column attribute to a nested result map, ignore it
            column = null;
          }
          if (propertyMapping.isCompositeResult()
              || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
              || propertyMapping.getResultSet() != null) {
            Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
            // issue #541 make property optional
            final String property = propertyMapping.getProperty();
            if (property == null) {
              continue;
            } else if (value == DEFERED) {
              foundValues = true;
              continue;
            }
            if (value != null) {
              foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
              // gcode issue #377, call setter on nulls (value is not 'found')
              metaObject.setValue(property, value);
            }
          }
        }
        return foundValues;
      }
      
      // collapseSingleResultList(multipleResults);  转换多级 list 为1级list
      @SuppressWarnings("unchecked")
      private List<Object> collapseSingleResultList(List<Object> multipleResults) {
        return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
      }

    至此,一条select 语句搞定!

    主要经历几个阶段:
      1. 加载配置
      2. 创建sqlSessionFactory
      3. 获取sqlSession
      4. jdbc连接
      5. 获取mapper
      6. 查询参数绑定
      7. 获取db结果
      8. 处理返回集字段映射
      9. 缓存结果
      10. 返回结果

  • 相关阅读:
    临时表空间占用大量空间(新建)
    学习总结
    sql:表关联方式
    11gR2 Clusterware 和 Grid Home
    sql分析常用查询
    通过 SSH 实现 TCP / IP 隧道(端口转发):使用 OpenSSH 可能的 8 种场景
    Fabric部署环境初始化(Centos 7)
    Fabric 学习路线
    代币智能合约(go)
    springboot切面编程范例
  • 原文地址:https://www.cnblogs.com/yougewe/p/9926638.html
Copyright © 2011-2022 走看看