Mybatis sql 语句简单分析

150 阅读2分钟

Mybatis sql 语句简单分析

今天这篇 Mapper 语句解析文章说实话,没有什么技术含量,就是简单说明一下都做了些什么,没有对具体的内容进行分析,详细分析放在后边。

Mapper 语句的解析主要是在 XMLStatementBuilder#parseStatementNode 方法上

首先查看一下代码:

public void parseStatementNode() {
        // sql 节点
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");
​
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { // <1>
            return;
        }
​
        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 
        /*
         <select id="findById">
            select <include refid = "baseSql"/>
            from table
         </select>
         替换成
         <select id="findById">
            select id, name
            from table
         </select>
         */
        // <2> 替换 sql 中的 include 节点为真正的 sql 内容
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); 
        includeParser.applyIncludes(context.getNode()); 
​
        // 获取 parameterType Class,可能是 null
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
​
        // TODO 目前不知道干什么的
        // select|insert|update|delete|sql 语句才有 lang 属性
        String lang = context.getStringAttribute("lang");
        LanguageDriver langDriver = getLanguageDriver(lang);
​
        // Parse selectKey after includes and remove them.
        // 处理 selectKey 节点 只有 insert 和 update 才有该节点
        // <3>
        processSelectKeyNodes(id, parameterTypeClass, langDriver);
​
        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        KeyGenerator keyGenerator;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        // 同意成为 全限定类目.id
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        // 判断是否存在 keyGenerator
        if (configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
            // 如果设置了 useGeneratedKeys 属性,属性值也是 true,则 keyGenerator = Jdbc3KeyGenerator.INSTANCE
            // 否则 keyGenerator = NoKeyGenerator.INSTANCE;
            keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
                configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
                ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
​
        // <4> 创建当前 sql 的 sqlSource
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        String resultType = context.getStringAttribute("resultType");
        Class<?> resultTypeClass = resolveClass(resultType);
        String resultMap = context.getStringAttribute("resultMap");
        String resultSetType = context.getStringAttribute("resultSetType");
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
        if (resultSetTypeEnum == null) {
            resultSetTypeEnum = configuration.getDefaultResultSetType();
        }
        String keyProperty = context.getStringAttribute("keyProperty");
        String keyColumn = context.getStringAttribute("keyColumn");
        String resultSets = context.getStringAttribute("resultSets");
​
        // <5> 创建 MappedStatement 并且保存在 Configuration#mappedStatements 中
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered,
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
  1. 因为 Mybatis 是支持多数据源和不同数据厂商的,在代码 <1> 处 this.requiredDatabaseId 在有值的情况下是 Configuration#databaseId 的值,如果与 sql 中的 databaseId 不匹配的话就不进行 sql 解析了。

  2. 在代码 <2> 处 是 对 标签进行替换,见

    [juejin.cn/post/708080…]  MybatisSql语句解析之Include节点的替换 

  3. 在代码 <3> 处,是对 节点中的 节点进行解析,后续会进行分析

  4. 在代码<4>处,是对当前 sql ,用于后续对该 sql 的执行

  5. 在代码<5>处,就是根据以上的参数创建一个 MappedStatement 并添加到 Configuration#mappedStatements中。

代码 <3><4>还有很多要分析,这里就先简单说明一下干了哪些事情,后续会对这些代码进行详细的分析。