MyBatis整合Spring的實現(13)

本章中分析insert元素的解析。java

1 配置文件node

<insert id="insert" parameterType="cn.vansky.schedule.time.menu.bo.Menu">
    <!--
      WARNING - @mbggenerated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Fri Aug 14 16:08:36 CST 2015.
    -->
    insert into tb_menu (menu_name, menu_remark, menu_parent_id, 
      menu_url, is_show, is_delete, 
      operation_user_name, operation_time)
    values (#{menuName,jdbcType=VARCHAR}, #{menuRemark,jdbcType=VARCHAR}, #{menuParentId,jdbcType=INTEGER}, 
      #{menuUrl,jdbcType=VARCHAR}, #{isShow,jdbcType=TINYINT}, #{isDelete,jdbcType=TINYINT}, 
      #{operationUserName,jdbcType=VARCHAR}, #{operationTime,jdbcType=TIMESTAMP})
</insert>

2 方法buildStatementFromContextsql

private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
     // 對一個SQL進行解析
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
}

上面是一個總的方法包括,insert、update、delete、select4中元素都會進入此方法。這裏也會捕獲一種異常,並把錯誤的解析放入Configuration(全局配置類)的Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>()。express

3 方法parseStatementNodeapache

public void parseStatementNode() {
    // insert
    String id = context.getStringAttribute("id");
    // null
    String databaseId = context.getStringAttribute("databaseId");
    // 第一次檢查這裏是不經過的,直接跳過
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
    // null 
    Integer fetchSize = context.getIntAttribute("fetchSize");
    // null
    Integer timeout = context.getIntAttribute("timeout");
    // null
    String parameterMap = context.getStringAttribute("parameterMap");
    // cn.vansky.schedule.time.menu.bo.Menu
    String parameterType = context.getStringAttribute("parameterType");
    // class cn.vansky.schedule.time.menu.bo.Menu
    Class<?> parameterTypeClass = resolveClass(parameterType);
    // null
    String resultMap = context.getStringAttribute("resultMap");
    // null
    String resultType = context.getStringAttribute("resultType");
    // null
    String lang = context.getStringAttribute("lang");
    // 獲取默認的處理對象
    // org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
    LanguageDriver langDriver = getLanguageDriver(lang);
    // null
    Class<?> resultTypeClass = resolveClass(resultType);
    // null
    String resultSetType = context.getStringAttribute("resultSetType");
    // PREPARED
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    // null
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

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

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    // 解析<include refid="Base_Column_List" />
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // 這裏經過下面分析獲取的SQL源(靜態SQL源),由於沒有動態標籤
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    // null
    String resultSets = context.getStringAttribute("resultSets");
    // null
    String keyProperty = context.getStringAttribute("keyProperty");
    // null
    String keyColumn = context.getStringAttribute("keyColumn");
    // org.apache.ibatis.executor.keygen.NoKeyGenerator
    KeyGenerator keyGenerator;
    // insert!selectKey
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    // cn.vansky.schedule.time.menu.dao.MenuMapper.insert!selectKey
    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);
  }

方法XMLLanguageDriver.createSqlSourceapp

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    // 入參Class及節點解析
    return builder.parseScriptNode();
}

5 方法XMLScriptBuilder.parseScriptNodeide

public SqlSource parseScriptNode() {
    // 解析動態標籤,if、where、for
    List<SqlNode> contents = parseDynamicTags(context);
    // 全部動態標籤集合類
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}

6 方法XMLScriptBuilder.parseDynamicTagsfetch

private List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    // 獲取動態標籤與內容列表
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      // 內容
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        // 內容SqlNode
        TextSqlNode textSqlNode = new TextSqlNode(data);
        // 動態修改的內容
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
        // 靜態內容
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        // 節點爲元素,也就是動態標籤where、if、for
        String nodeName = child.getNode().getNodeName();
        // 獲取動態標籤處理器WhereHandler、ForEachHandler
        NodeHandler handler = nodeHandlers.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        // 處理器處理內容,並把處理的內容加入到SQLNode
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }

方法5中有2中數據源DynamicSqlSource(動態SQL源)和RawSqlSource(靜態SQL源),動態SQL源就是包括where、if、for,靜態SQL源就是隻包括SQL語句,下面就簡單分析一下2中SQL源。ui

RawSqlSource(靜態SQL源)
this

public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
}
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    // 參數處理Mapping
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    // 解析XML中的SQL語句,#{menuName,jdbcType=VARCHAR},轉換成?及對應的處理類型ParameterMapping
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

這裏直接解析並生成StaticSqlSource,包括SQL語句及對應的處理類型。

7.1 ParameterMapping對應的menuName

/** 全局配置類 */
private Configuration configuration;
/** menuName */
private String property;
/** IN:輸入 */
private ParameterMode mode;
/** class java.lang.String */
private Class<?> javaType = Object.class;
/** VARCHAR */
private JdbcType jdbcType;
/** null */
private Integer numericScale;
/** StringTypeHandler */
private TypeHandler<?> typeHandler;
/** null */
private String resultMapId;
/** null */
private String jdbcTypeName;
/** null */
private String expression;

DynamicSqlSource(動態SQL源)

public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
}

很明顯這裏沒有進行生成SQL,那就是在具體獲取SQL時進行生成SQL

9 方法addMappedStatement

此方法只是賦值,沒有什麼特殊處理,因此此方法不在分析,能夠自行研究。

總結 

附上圖片MappedStatement屬性值

下面給出動態SQL源的MappedStatement屬性值

<insert id="insertSelective" parameterType="cn.vansky.schedule.time.menu.bo.Menu">
    <!--
      WARNING - @mbggenerated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Fri Aug 14 16:08:36 CST 2015.
    -->
    insert into tb_menu
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="menuName != null">
        menu_name,
      </if>
      <if test="menuRemark != null">
        menu_remark,
      </if>
      <if test="menuParentId != null">
        menu_parent_id,
      </if>
      <if test="menuUrl != null">
        menu_url,
      </if>
      <if test="isShow != null">
        is_show,
      </if>
      <if test="isDelete != null">
        is_delete,
      </if>
      <if test="operationUserName != null">
        operation_user_name,
      </if>
      <if test="operationTime != null">
        operation_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="menuName != null">
        #{menuName,jdbcType=VARCHAR},
      </if>
      <if test="menuRemark != null">
        #{menuRemark,jdbcType=VARCHAR},
      </if>
      <if test="menuParentId != null">
        #{menuParentId,jdbcType=INTEGER},
      </if>
      <if test="menuUrl != null">
        #{menuUrl,jdbcType=VARCHAR},
      </if>
      <if test="isShow != null">
        #{isShow,jdbcType=TINYINT},
      </if>
      <if test="isDelete != null">
        #{isDelete,jdbcType=TINYINT},
      </if>
      <if test="operationUserName != null">
        #{operationUserName,jdbcType=VARCHAR},
      </if>
      <if test="operationTime != null">
        #{operationTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
</insert>

其實這裏屬性都差很少,主要是SqlSource不同,那麼下來就看它的屬性。

相關文章
相關標籤/搜索