mybatis 映射文件加載

之前一直是作遊戲後端開發,如今開始轉應用開發,初學mybatis,若是有理解錯誤的地方,感謝你們的指出node

 

第一次搭建SSM框架,對dao層接口和sql的映射文件怎麼聯繫到一塊兒的很好奇,查了一些資料,本身也跟的源碼走了一邊,大體有個思路,寫下來spring

先看spriing和mybatis的配置文件sql

 

咱們掃描映射文件的時候,是用了一個org.mybatis.spring.SqlSessionFactoryBean的類,而且將每個xml文件加載到mapperLocations這個變量中,如今打開這個類,跳轉到buildSqlSessionFactory這個方法裏,能夠找到如下代碼後端

進入parse函數mybatis

public void parse() {
    //若是configuration對象還沒加載xml配置文件(避免重複加載,其實是確認是否解析了mapper節點的屬性及內容,
    //爲解析它的子節點如cache、sql、select、resultMap、parameterMap等作準備),
    //則從輸入流中解析mapper節點,而後再將resource的狀態置爲已加載
    if (!configuration.isResourceLoaded(resource)) {
    //解析xml的每個子節點 configurationElement(parser.evalNode(
"/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } //解析在configurationElement函數中處理resultMap時其extends屬性指向的父對象還沒被處理的<resultMap>節點 parsePendingResultMaps(); //解析在configurationElement函數中處理cache-ref時其指向的對象不存在的<cache>節點(若是cache-ref先於其指向的cache節點加載就會出現這種狀況) parsePendingChacheRefs(); //同上,若是cache沒加載的話處理statement時也會拋出異常 parsePendingStatements(); }
private void configurationElement(XNode context) {
    try {
      //獲取mapper節點的namespace屬性
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //設置當前namespace
      builderAssistant.setCurrentNamespace(namespace);
      //解析mapper的<cache-ref>節點
      cacheRefElement(context.evalNode("cache-ref"));
      //解析mapper的<cache>節點
      cacheElement(context.evalNode("cache"));
      //解析mapper的<parameterMap>節點
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析mapper的<resultMap>節點
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析mapper的<sql>節點
      sqlElement(context.evalNodes("/mapper/sql"));
      //使用XMLStatementBuilder的對象解析mapper的<select>、<insert>、<update>、<delete>節點,
      //mybaits會使用MappedStatement.Builder類build一個MappedStatement對象,
      //因此mybaits中一個sql對應一個MappedStatement
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

configurationElement函數幾乎解析了mapper節點下全部子節點,至此mybaits解析了mapper中的全部節點,並將其加入到了Configuration對象中提供給sqlSessionFactory對象隨時使用app

咱們進入buildStatementFromContext函數框架

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

而後繼續進buildStatementFromContext函數函數

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
     //解析 statementParser.parseStatementNode(); }
catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }

對每個增刪改查的節點進行解析,看看parseStatementNodefetch

public void parseStatementNode() {
   //這個id就是咱們執行sql時指定的名字
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

這個函數解析了各個子節點的具體值 而後傳入addMappedStatement函數ui

 public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    //咱們生成一個statement 對象
    MappedStatement statement = statementBuilder.build();
    //將statement 對象添加到configuration中
    configuration.addMappedStatement(statement);
    return statement;
  }

看一下configuration.addMappedStatement方法

  public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }

咱們最後根據id也就是sql執行的名稱  存入mappedStatements這個容器中

咱們還能夠在這個類中找到一個方法

  public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }

這個方法是經過sql名稱返回一個MappedStatement對象,而這個對象是能夠執行sql的,因此 咱們能夠猜測,當咱們在執行dao層接口的時候,最後必定是調用到這裏

以上就是映射文件加載大體過程

參考與感謝

http://blog.csdn.net/flashflight/article/details/43926091

相關文章
相關標籤/搜索