2.6、mybatis源碼分析之初始化過程(1)

  • 前面介紹了myabtis的基礎組件部分,感覺起來非常的零散,沒有系統(tǒng)性。但是前面部分的內(nèi)容是基礎,了解了前面基礎組件部分的內(nèi)容,在跟著流程去看源碼,就不會云里霧里了,如果不看前面的基礎,蒙著頭往mybatis源碼里面去撞,基本會迷路摸不清方向。下面我們將跟著myabtis的執(zhí)行流程去分析源碼,首先要分析的是mybatis的初始化過程。
  • 在將mybatis初始化過程之前,先來看看mybatis的的簡單執(zhí)行代碼。
//開始初始化
String resource = "mybatis-config.xml";  
InputStream inputStream = Resources.getResourceAsStream(resource);  
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                      .build(inputStream); 
//獲取會話并開始執(zhí)行sql 
SqlSession sqlSession = sqlSessionFactory.openSession();  
List list = sqlSession.selectList("com.huya.qiu.mapper.UserMapper.getAllUser");

一、mybatis初始化過程

  • *在mybatis初始化過程中主要完成的是config配置、mapper配置和相關注解信息的解析,初始化入口就是SqlSessionFactoryBuilder.buidler()方法。
  public SqlSessionFactory build(Reader reader, String environment, 
Properties properties) {
    try {
     1、讀取配置
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, 
environment, properties);
      2、解析配置文件得到Configuration對象,創(chuàng)建DefaultSqlSessionFactory對象
      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.
      }
    }
  }

從上面可以看出,build方法主要做的就是兩件事(1)創(chuàng)建XMLConfigBuilder 對象(2)解析配置文件得到Configuration對象,創(chuàng)建DefaultSqlSessionFactory對象。下面針對這兩個步驟進行詳細的分析。

1、XMLConfigBuilder(解析核心配置文件)

  • 扮演具體建造者的角色,主要負責解析config配置文件的解析并繼承BaseBuilder類。
    1、BaseBuilder
    是mybatis中有關建造者的抽象類,扮演著建造者模式中的建造者接口角色,具體的類結構如圖,BaseBuilder擁有眾多的實現(xiàn)類,這些實現(xiàn)類都扮演著具體場合的建造者角色:


    BaseBuilder類結構圖
public abstract class BaseBuilder {
//引用configuration對象
  protected final Configuration configuration;
  //TypeAliasRegistry會記錄別名
  protected final TypeAliasRegistry typeAliasRegistry;
  //TypeHandlerRegistry加注冊類型轉換器(自定義或者默認)
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
  ...提供了一系列的resolveTypeHandler、resolveResultSetType等抽象方法

2、XMLConfigBuilder具體實現(xiàn)

public class XMLConfigBuilder extends BaseBuilder {
    //標志是否已經(jīng)解析過配置文件
  private boolean parsed;
  //用于加息配置文件的XpathParse對象
  private final XPathParser parser;
  //<enviroment>標簽
  private String environment;
  //負責創(chuàng)建和緩存Reflector對象
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
//構造函數(shù)
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }
//解析config配置文件的入口
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //查找<configuration>節(jié)點,開始解析
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  
  private void parseConfiguration(XNode root) {
    try {
      //解析<properties>節(jié)點
      propertiesElement(root.evalNode("properties"));
      //解析<settings>節(jié)點
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      //解析<typeAliases>節(jié)點
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析<plugins>節(jié)點
      pluginElement(root.evalNode("plugins"));
      //解析<objectFactory>節(jié)點
      objectFactoryElement(root.evalNode("objectFactory"));
      //解析<objectWrapperFactory>節(jié)點
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //解析<reflectorFactory>節(jié)點
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);//將值設置到setting中
      //解析<environments>節(jié)點
      environmentsElement(root.evalNode("environments"));
      //解析<databaseIdProvider>節(jié)點
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析<typeHandlers>節(jié)點
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析<mappers>節(jié)點
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
  • parseConfiguration方法是最終執(zhí)行配置文件解析的地方,從方法中可以看出解析的順序是按著核心配置文件中節(jié)點配置順序來的,因此如果我們在配置核心配置文件的時候,是要按著節(jié)點順序來的。下面針對方法中每一個節(jié)點的解析詳細介紹:
    (1)解析<properties>節(jié)點
  • 解析后形成Properties對象,之后將該字段設置到XpathParser和Configuration的variables字段中。
private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
    //解析相應的name和value屬性記錄到properties中
      Properties defaults = context.getChildrenAsProperties();
      //解析reource和url屬性用于確定配置文件位置,且這兩個不能同時存在
      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);
      }
      //更新xpathParse和configuration的variables字段
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

(2)解析<settings>節(jié)點

  • settings設置會改變mybatis的運行時行為,在初始化的時候這些全局配置信息都會被記錄到configuration對象的對應屬性中
private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    //解析settings子節(jié)點的name和value屬性并返回properties對象
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    //創(chuàng)建metaClass
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
    //檢測key指定的屬性在configuration類中是都有對應的setter方法
      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;
  }

(3)解析解析<typeAliases>節(jié)點

  • typeAliases與別名有關,前面知識可知解析后在TypeAliasRegistry中注冊。
private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      //處理package節(jié)點
        if ("package".equals(child.getName())) {
        //獲取包名
          String typeAliasPackage = child.getStringAttribute("name");
          //注冊包下的所有別名,放入hash中
          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);
          }
        }
      }
    }
  }

(4)解析<typeHandlers>節(jié)點

  • 通過TypeHandlerRegistry對象完成TypeHandler的注冊實現(xiàn)原理和上面的相似

(5)解析<plugins>節(jié)點

  • 插件是mybatis提供的擴展機制之一,用于通過添加自定義的插件對sql語句執(zhí)行過程中某一個點進行攔截。自定義插件需要實現(xiàn)Interceptor接口并通過注解指定需要攔截的方法簽名
 private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      //獲取<interceptor>節(jié)點的屬性的值
        String interceptor = child.getStringAttribute("interceptor");
        //獲取節(jié)點下<properties>配置信息形成properties對象
        Properties properties = child.getChildrenAsProperties();
        //進行屬性別名注冊,并實例化攔截器對象
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //設置屬性
        interceptorInstance.setProperties(properties);
        //加載到configuration
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
  protected Class<?> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
   protected Class<?> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }

(6)解析<objectFactory>,<objectWrapperFactory>,<reflectorFactory>等節(jié)點

  • 邏輯和解析<plugins>節(jié)點差不多

(7)解析<environments>節(jié)點

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
      //未指定enviroment字段則就只用default指定
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        //與environment字段匹配
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
         //創(chuàng)建datasourcefactory和datasource
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //建造者模式
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
              //記錄到environment字段中
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }
  private boolean isSpecifiedEnvironment(String id) {
    if (environment == null) {
      throw new BuilderException("No environment specified.");
    } else if (id == null) {
      throw new BuilderException("Environment requires an id attribute.");
    } else if (environment.equals(id)) {
      return true;
    }
    return false;
  }

(8)解析<databaseIdProvider>節(jié)點

  • 可以通過databaseIdProvider定義所支持的數(shù)據(jù)庫產(chǎn)品databaseId,然后在映射配置文件中定義sql語句節(jié)點時候,通過databaseid指定該sql語句應用的數(shù)據(jù)庫產(chǎn)品。實現(xiàn)幫助開發(fā)人員屏蔽多種數(shù)據(jù)庫產(chǎn)品在sql語句方面的差異。
  • mybatis中根據(jù)datasource確定數(shù)據(jù)庫產(chǎn)品類型,然后在解析映射配置文件時候,加載不帶databaseId屬性和帶有匹配當前數(shù)據(jù)庫databaseId屬性的所有sql語句,如果同時找到name拋棄不帶databaseId的相同語句。
 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) {
    //通過datasource獲取databaseId,記錄到configuration.databaseId字段中
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      configuration.setDatabaseId(databaseId);
    }
  }

(9)解析<mapper>節(jié)點

  • 在加載config配置文件時候,<mapper>節(jié)點節(jié)點會告訴mybatis去哪些位置查找映射配置文件以及使用配置注解標識的接口
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      //package子節(jié)點
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          //向mapperRegistry注冊mapper接口
          configuration.addMappers(mapperPackage);
        } else {
        //回去resource、url、class屬性,三個屬性互斥
          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);
            //創(chuàng)建XMLMapperBuilder,解析映射配置文件
            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.");
          }
        }
      }
    }
  }

二、XMLMapperBuilder(解析映射配置文件)

  • 負責解析映射配置文件,繼承了BaseBuilder抽象類。也是具體的建造者角色,parse方法是其入口
public void parse() {
//判斷是否已經(jīng)加載了該映射文件
    if (!configuration.isResourceLoaded(resource)) {
    //處理<mapper>節(jié)點
      configurationElement(parser.evalNode("/mapper"));
      //將resource添加到集合中保存,記錄加載過的映射文件
      configuration.addLoadedResource(resource);
      //注冊mapper接口
      bindMapperForNamespace();
    }
//處理configurationElement中解析失敗的<resultMap>節(jié)點
    parsePendingResultMaps();
    //處理configurationElement中解析失敗的<cache-ref>節(jié)點
    parsePendingCacheRefs();
    //處理configurationElement中解析失敗的sql語句節(jié)點
    parsePendingStatements();
  }
  //封裝了每一個節(jié)點的解析過程
private void configurationElement(XNode context) {
    try {
    //1.獲取<mapper>節(jié)點的namespace屬性,并記錄命名空間
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      //2.解析cache-ref節(jié)點
      cacheRefElement(context.evalNode("cache-ref"));
      //3.解析cache節(jié)點
      cacheElement(context.evalNode("cache"));
      //已經(jīng)廢棄
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //4.解析resultMap節(jié)點
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //5.解析sql節(jié)點
      sqlElement(context.evalNodes("/mapper/sql"));
      //6.解析select|insert|update|delete等sql節(jié)點
      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);
    }
  }
  • 從方法中可以得出配置文件的解析最終也是歸屬到單個節(jié)點的解析
    (1)解析cache節(jié)點
  • mybatis默認沒有開啟二級緩存,要開啟需要在映射配置文件中添加cache節(jié)點
private void cacheElement(XNode context) throws Exception {
    if (context != null) {
    //獲取cache節(jié)點的type屬性,默認是PERPETUAL
      String type = context.getStringAttribute("type", "PERPETUAL");
      //查找type屬性對應的cache接口歐
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      //獲取eviction屬性默認是LRU
      String eviction = context.getStringAttribute("eviction", "LRU");
      //解析eviction屬性指定的cache裝飾器類型
      Class<? extends Cache> evictionClass = 
typeAliasRegistry.resolveAlias(eviction);
      //獲取flushInterval屬性默認是null
      Long flushInterval = context.getLongAttribute("flushInterval");
      //獲取size屬性值
      Integer size = context.getIntAttribute("size");
      //獲取readOnly屬性值
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      //通過builderAssistant創(chuàng)建Cache對象,并添加到caches集合中保存
      builderAssistant.useNewCache(typeClass, evictionClass, 
flushInterval, size, readWrite, blocking, props);
    }
  }
------------------------------------------------------------------------------
builderAssistant類中的useNewCache方法
  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
      //利用構建器構建出緩存
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }

(2)解析cache-ref

  • 如果想實現(xiàn)一個二級緩存在多個namespace之間共用可以配置cache-ref節(jié)點
    (3)解析resultMap節(jié)點
  • resultMap定義了結果集與javabean之間的映射規(guī)則
    ResultMap對象

每一個resultMap節(jié)點會被映射成一個ResultMap對象,而每一個節(jié)點所定義的映射關系使用ResultMapping對象表示
也就是說一個ResultMap對應resultMap節(jié)點,ResultMapping對應resultMap屬性

public class ResultMap {
  private Configuration configuration;

  private String id;//節(jié)點id
  private Class<?> type;//節(jié)點類型
  private List<ResultMapping> resultMappings;//其他映射關系集合
  private List<ResultMapping> idResultMappings;//記錄映射關系帶有id標識的映射關系,id節(jié)點和constructor節(jié)點的idArg子節(jié)點
  private List<ResultMapping> constructorResultMappings;//constructor所有子節(jié)點
  private List<ResultMapping> propertyResultMappings;//不帶有constructor標識的映射關系
  private Set<String> mappedColumns;//記錄所有涉及column屬性的集合
  private Set<String> mappedProperties;//記錄所有propertes屬性的集合
  private Discriminator discriminator;//鑒別器節(jié)點
  private boolean hasNestedResultMaps;//含有resultMap屬性不含有resultSet屬性則為true
  private boolean hasNestedQueries;//存在select屬性,為true
  private Boolean autoMapping;//是否開啟自動映射
...

resultMapping對象

  • 記錄了結果集中的一列與JavaBean中的一個屬性之間的映射關系,也對應resultMap中的屬性
public class ResultMapping {

  private Configuration configuration;
  private String property;//對應節(jié)點的property屬性,表示的是該列進行映射的屬性
  private String column;//對應節(jié)點的column屬性,表示從數(shù)據(jù)庫中得到的列名或者列名的別名
  private Class<?> javaType;//對應節(jié)點的javaType屬性,表示的是一個JavaBean的完全限定名,后者一個類型別名
  private JdbcType jdbcType;//對應節(jié)點的jdbcType屬性,表示映射列的JDBC類型
  private TypeHandler<?> typeHandler;//對應節(jié)點的typeHandler,表示類型處理器
  private String nestedResultMapId;//對應節(jié)點的resultMap屬性,通過id引用另一個resultMap節(jié)點的定義
  private String nestedQueryId;//對應節(jié)點的select屬性,通過id應用到另一個select節(jié)點定義
  private Set<String> notNullColumns;
  private String columnPrefix;
  private List<ResultFlag> flags;
  private List<ResultMapping> composites;
  private String resultSet;//對應resultSet屬性
  private String foreignColumn;
  private boolean lazy;//是否延遲加載,對應fetchType屬性
...

resultMapElements方法解析<resultMap>節(jié)點

 private void resultMapElements(List<XNode> list) throws Exception {
    for (XNode resultMapNode : list) {
      try {
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
  }
  //處理每一個resultMap節(jié)點
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    //獲取id屬性,默認會拼裝所有父節(jié)點的id或者value后者Property屬性值
    String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
        //獲取type屬性,表示結果集將被映射成type指定類型的對象(也就是如果有ofType、resultType等最后映射成type)
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
     //后去extends屬性,指定繼承關系
    String extend = resultMapNode.getStringAttribute("extends");
    //是否是自動映射,及自動查找與列名同名的屬性名
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    //加息type類型
    Class<?> typeClass = resolveClass(type);
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    resultMappings.addAll(additionalResultMappings);
    //處理resultMap子節(jié)點
    List<XNode> resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
    //處理constructor子節(jié)點
      if ("constructor".equals(resultChild.getName())) {
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
      //處理id、result、association、collection等子節(jié)點
        List<ResultFlag> flags = new ArrayList<ResultFlag>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        //創(chuàng)建ResultMapping對象,添加到resultmappings集合中保存
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
    //創(chuàng)建ResultMap對象,添加到Configuration.resultmaps集合中去
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }
//創(chuàng)建相應的ResultMapping對象
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
    String property;
    if (flags.contains(ResultFlag.CONSTRUCTOR)) {
      property = context.getStringAttribute("name");
    } else {
      property = context.getStringAttribute("property");
    }
    String column = context.getStringAttribute("column");
    String javaType = context.getStringAttribute("javaType");
    String jdbcType = context.getStringAttribute("jdbcType");
    String nestedSelect = context.getStringAttribute("select");
    String nestedResultMap = context.getStringAttribute("resultMap",
        processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
    String notNullColumn = context.getStringAttribute("notNullColumn");
    String columnPrefix = context.getStringAttribute("columnPrefix");
    String typeHandler = context.getStringAttribute("typeHandler");
    String resultSet = context.getStringAttribute("resultSet");
    String foreignColumn = context.getStringAttribute("foreignColumn");
    boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
    Class<?> javaTypeClass = resolveClass(javaType);
    @SuppressWarnings("unchecked")
    Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
  }

整個解析過程還是很復雜的,建議去看源碼,這里就不貼代碼了(ps:自己這一部分也是看的半懂,后面有時間回看一遍)

(4)sql節(jié)點解析

  • 使用sql節(jié)點可以增加sql語句片段的重復利用,只需要使用include引入就好了
 private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    for (XNode context : list) {//遍歷sql節(jié)點
    //獲取databaseiId
      String databaseId = context.getStringAttribute("databaseId");
      String id = context.getStringAttribute("id");
      //為id添加命名空間
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
      //記錄到sqlFragments集合中保存
        sqlFragments.put(id, context);
      }
    }
  }

(5)sql語句相關節(jié)點解析

待定...


?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 6,248評論 0 4
  • 1 Mybatis入門 1.1 單獨使用jdbc編程問題總結 1.1.1 jdbc程序 上邊使...
    哇哈哈E閱讀 3,417評論 0 38
  • 今天清晨為學堂的孩子做早餐,煮芝麻湯圓,溜饅頭,拌涼菜,感覺感官更靈敏了。黃瓜的清新。紫甘藍的辛甜,用姜粉、醋和新...
    依水聽心閱讀 824評論 0 3
  • 人與人之間的經(jīng)歷不同,對事情的看法也不盡相同。 曾經(jīng),看到過一句話:你簡單了,世界也會簡單。 那時身處困境,一度將...
    Finchroot閱讀 263評論 0 1

友情鏈接更多精彩內(nèi)容