天天看點

Mybatis源碼解析【Mapper檔案解析】

1、解析過程中,一些重要的類,如下:

1、XMLMapperBuilder,一個mapper檔案一個builder
2、MapperBuilderAssistant,XMLMapperBuilder的助手
3、Cache,緩存對象
4、XMLStatementBuilder,一條SQL一個MapperStatement
5、KeyGenerator,主鍵生成政策
6、XMLScriptBuilder,SQL内容解析Builder
7、DynamicSqlSource,具有動态标簽的SQL;RawSqlSource,不具有動态标簽的SQL
8、GenericTokenParser,占位符解析${},#{}
           

2、入口看到XMLConfigBuilder.parseConfiguration(),解析Mybatis配置檔案中的标簽

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"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
           

3、next,看到mapperElement()方法,進入到方法裡,可以看到有幾種解析mapper标簽的方式(url,resource,mapperClass)

4、重點看XMLMapperBuilder中的parse()方法,看到了builderAssistant助手,隻是為了記錄資訊,友善在其它地方能擷取到目前mapper的資訊,比如說namespace,mapperstatement等等

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);
      //  目前名稱空間是否和其它namespace使用同一個緩存
      cacheRefElement(context.evalNode("cache-ref"));
      //  目前mapper是否開啟緩存  使用namespace作為cacheId
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      //  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);
    }
  }
           

5、如果mapper.xml中有cache-ref或者cache,就會用到Mybatis中的二級緩存,這個另外再說,因為緩存又是一個子產品。next,重點還是看buildStatementFromContext(),解析curd

6、點進去,就能看到XMLStatementBuilder了,Mybatis在解析中大量使用建造者模式,可能是因為屬性比較多,用set方式太雞肋;顧名思義,XMLStatementBuilder就是建構MapperStatement

7、看到XMLStatementBuilder中的parseStatementNode(),這個就是解析sql标簽的方法,這裡主要看SqlSource的解析:createSqlSource

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
  }
           

8、XMLScriptBuilder,sql内容解析的builder;DynamicSqlSource和RawSqlSource都實作了SqlSource

public SqlSource parseScriptNode() {
    // 解析動态标簽
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource = null;
    if (isDynamic) {
      //  sql中是否有動态标簽  if  where foreach ...
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      //  沒有動态标簽
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
           

9、parseDynamicTags,動态标簽解析,裡面可以看到不同的動态标簽會有不同的handler,分别處理,最後解析成SqlNode添加到容器裡,解析表達式會用到Ognl

10、上面是大概的mapper檔案解析内容,最後說說主鍵生成政策KeyGenerator,是一個接口,裡面有兩個方法,

// 針對Sequence主鍵而言,在執行insert sql前必須指定一個主鍵值給要插入的記錄
  void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

  // 針對自增主鍵的表,在插入時不需要主鍵,而是在插入過程自動擷取一個自增的主鍵
  void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);