Mybatis-LanguageDriver源码解析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. Mybatis-Interceptor源码解析

LanguageDriver

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;

/**
 * 语言驱动
 */
public interface LanguageDriver {

  /**
   * Creates a {@link ParameterHandler} that passes the actual parameters to the the JDBC statement.
   * 创建 {@link ParameterHandler} 通过JDBC的实际参数
   * @param mappedStatement The mapped statement that is being executed 映射的正在执行语句
   * @param parameterObject The input parameter object (can be null) 输入的参数对象(可以为空)
   * @param boundSql The resulting SQL once the dynamic language has been executed. 生成的SQL,一旦动态语言被执行
   * @return
   * @author Frank D. Martinez [mnesarco]
   * @see DefaultParameterHandler
   */
  ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);

  /**
   * Creates an {@link SqlSource} that will hold the statement read from a mapper xml file.
   * It is called during startup, when the mapped statement is read from a class or an xml file.
   * 创建一个{@link SqlSource} 读取映射XML文件
   * @param configuration The MyBatis configuration MyBatis配置
   * @param script XNode parsed from a XML file
   * @param parameterType input parameter type got from a mapper method or specified in the parameterType xml attribute. Can be null.
   *                      输入参数类型从一个xml类型映射器参数中指定的方法或属性。可以为空。;
   * @return
   */
  SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);

  /**
   * Creates an {@link SqlSource} that will hold the statement read from an annotation.
   * It is called during startup, when the mapped statement is read from a class or an xml file.
   * 创建一个{@link SqlSource} 从一个字解里,它在启动期间,当映射语句从一个类或读取一个xml文件被调用。;
   * @param configuration The MyBatis configuration
   * @param script The content of the annotation
   * @param parameterType input parameter type got from a mapper method or specified in the parameterType xml attribute. Can be null.
   *                      输入参数类型从一个xml类型映射器参数中指定的方法或属性。可以为空。;
   * @return
   */
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);

}

XMLLanguageDriver

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.xmltags;

import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;

/**
 * XML语言驱动
 * <p>
 *     Mybatis默认XML驱动类为XMLLanguageDriver,其主要作用于解析select|update|insert|delete节点为完整的SQL语句。
 * </p>
 * @author Eduardo Macarron
 */
public class XMLLanguageDriver implements LanguageDriver {

  @Override
  public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  }

  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    /***
     *  当sql中包含有${}时,就认为是动态SQL
     *       或者 当DML标签存在动态SQL标签名,如&lt;if&gt;,&lt;trim&gt;等,就认为是动态SQL
     *  ,如果是动态返回 {@link DynamicSqlSource} ,否则 {@link RawSqlSource}
     */
    return builder.parseScriptNode();
  }

  @Override
  public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
    // issue #3 此处兼容XML方式的解析,条件以<script>为头结点
    if (script.startsWith("<script>")) {
      XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
      return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
    } else {
      // issue #127
      /**
       * 解析Configuration#variable变量,将有${...}形式的字符串转换成对应字符串,
       *    eg:  '${first_name},${initial},${last_name}' => 'James,T,Kirk'
       */
      script = PropertyParser.parse(script, configuration.getVariables());
      TextSqlNode textSqlNode = new TextSqlNode(script);
      //根据TextSqlNode的内部属性isDynamic来进行解析帮助类的分配
      if (textSqlNode.isDynamic()) {
        return new DynamicSqlSource(configuration, textSqlNode);
      } else {
        return new RawSqlSource(configuration, script, parameterType);
      }
    }
  }

}

XMLScriptBuilder

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.xmltags;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * XML脚本构建器
 * @author Clinton Begin
 */
public class XMLScriptBuilder extends BaseBuilder {

  /**
   * DML标签
   */
  private final XNode context;

  /**
   * 是否是动态SQL脚本.
   * <ol style='margin-top:0px'>
   *     <li>当sql中包含有${}时,就认为是动态SQL</li>
   *     <li>当DML标签存在动态SQL标签名,如&lt;if&gt;,&lt;trim&gt;等,就认为是动态SQL</li>
   * </ol>
   */
  private boolean isDynamic;

  /**
   * DML标签的参数类型
   */
  private final Class<?> parameterType;

  /**
   * 标签节点处理映射,key=动态SQL标签名,如&lt;if&gt;,&lt;trim&gt;等,value=动态SQL标签处理类实例
   */
  private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();

  /**
   *
   * @param configuration mybatis全局配置
   * @param context DML标签
   */
  public XMLScriptBuilder(Configuration configuration, XNode context) {
    this(configuration, context, null);
  }

  /**
   *
   * @param configuration mybatis全局配置
   * @param context DML标签
   * @param parameterType 参数类型
   */
  public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
    super(configuration);
    this.context = context;
    this.parameterType = parameterType;
    initNodeHandlerMap();
  }

  /**
   * 将所有的动态SQL标签处理器添加到 {@link #nodeHandlerMap}
   */
  private void initNodeHandlerMap() {
    nodeHandlerMap.put("trim", new TrimHandler());
    nodeHandlerMap.put("where", new WhereHandler());
    nodeHandlerMap.put("set", new SetHandler());
    nodeHandlerMap.put("foreach", new ForEachHandler());
    nodeHandlerMap.put("if", new IfHandler());
    nodeHandlerMap.put("choose", new ChooseHandler());
    nodeHandlerMap.put("when", new IfHandler());
    nodeHandlerMap.put("otherwise", new OtherwiseHandler());
    nodeHandlerMap.put("bind", new BindHandler());
  }

  /**
   * 解析脚本标签节点
   * @return
   *  当sql中包含有${}时,就认为是动态SQL
   *      或者 当DML标签存在动态SQL标签名,如&lt;if&gt;,&lt;trim&gt;等,就认为是动态SQL
   * ,如果是动态返回 {@link DynamicSqlSource} ,否则 {@link RawSqlSource}
   */
  public SqlSource parseScriptNode() {
    //解析DML标签下的子节点,封装成MixedSqlNode
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      //只创建DynamicSqlSource对象。完整可执行的sql,需要在调用getBoundSql(Parameter)方法时才能组装完成。
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      /**
       * 调用 SqlSourceBuilder类将"#{xxx}“ 替换为占位符”?",并绑定ParameterMapping,
       * 最后返回的RawSqlSource中持有一个由SqlSourceBuilder构建的SqlSource对象。
       */
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

  /**
   * 解析动态SQL标签,对 {@code node} 的子节点和文本节点进行解析,
   *  这里会检测是否属于动态就会对isDynamic设置对应的值
   * @param node DML标签或者是动态SQL标签
   * @return 混合着 {@code node} 的子节点的MixedSqlNode实例
   */
  protected MixedSqlNode parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      /**
       * XNode.newXNode(Node node):新建一个XNode,XNode只是mybatis对Node实例的封装,用于方便操作。
       * 并不是添加node节点的功能
       */
      XNode child = node.newXNode(children.item(i));
      /**
       * Node.CDATA_SECTION_NODE:代表文档中的 CDATA 部(不会由解析器解析的文本)
       * Node.TEXT_NODE:代表元素或属性中的文本内容
       */
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        //获取文本节点的文本内容
        String data = child.getStringBody("");
        //文本SQL节点。用传入的实际参数对象中的属性值对${}进行直接替换,并且不会进行任何检查!
        TextSqlNode textSqlNode = new TextSqlNode(data);
        //当sql中包含有${}时,就认为是动态SQL.下面就是根据是否是动态SQL来添加对应SqlNode类型
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          //StaticTextSqlNode:静态文本SQL节点,其apply方法使得只是将存储的文本内容直接添加到DynamicContext对象中
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 Node.ELEMENT_NODE:代表元素
        String nodeName = child.getNode().getNodeName();
        //根据标签名找到对应的NodeHandler
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return new MixedSqlNode(contents);
  }

  /**
   * 标签节点处理器,解析标签节点,封装到对应sqlNode中
   */
  private interface NodeHandler {
    /**
     * 解析标签节点,封装到对应sqlNode中,并将sqlNode添加到 {@code targetContents}
     * @param nodeToHandle 动态SQL标签名,如&lt;if&gt;,&lt;trim&gt;等
     * @param targetContents 解析完 {@code nodeToHandle}SqlNode后,
     */
    void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
  }

  /**
   * &lt;bind&gt;处理器:元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文
   * <p>如:</p>
   * <p>
   *     &lt;bind name="pattern" value="'%' + _parameter.getTitle() + '%'" /&gt;
   * </p>
   */
  private class BindHandler implements NodeHandler {
    public BindHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      //获取bind标签的name属性值
      final String name = nodeToHandle.getStringAttribute("name");
      //获取bind标签的value属性值
      final String expression = nodeToHandle.getStringAttribute("value");
      //OGNL解析
      final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
      targetContents.add(node);
    }
  }

  /**
   * trim标签处理器
   */
  private class TrimHandler implements NodeHandler {
    public TrimHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      //解析trim标签下的字节点,封装成MixedSqlNode
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      //获取trim标签的prefix属性,给sql语句拼接的前缀
      String prefix = nodeToHandle.getStringAttribute("prefix");
      /**
       * 获取trim标签的prefixOverrides属性
       * 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,
       * 假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
       */
      String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
      //获取trim标签的suffix属性,给sql语句拼接的后缀
      String suffix = nodeToHandle.getStringAttribute("suffix");
      //获取trim标签的suffixOverride,去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定
      String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
      //Trim标签的功能逻辑交给TrimSqlNode
      TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
      targetContents.add(trim);
    }
  }

  /**
   * where标签处理器
   */
  private class WhereHandler implements NodeHandler {
    public WhereHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      //解析where标签下的字节点,封装成MixedSqlNode
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      //where标签的功能逻辑交给whereSqlNode处理
      WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
      targetContents.add(where);
    }
  }

  /**
   * Set标签处理器
   */
  private class SetHandler implements NodeHandler {
    public SetHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      //解析set标签下的子节点,封装成MixedSqlNode
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      //set标签的功能逻辑交给whereSqlNode处理
      SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
      targetContents.add(set);
    }
  }

  /**
   * forEach标签接收处理器
   */
  private class ForEachHandler implements NodeHandler {
    public ForEachHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      ///解析forEach标签下的子节点,封装成MixedSqlNode
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      //获取foreach标签的collection属性,循环的集合对象名
      String collection = nodeToHandle.getStringAttribute("collection");
      //获取foreach标签的item属性,集合对象的元素名
      String item = nodeToHandle.getStringAttribute("item");
      //获取foreach标签的index属性,在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
      String index = nodeToHandle.getStringAttribute("index");
      //获取foreach标签的open属性,foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选。
      String open = nodeToHandle.getStringAttribute("open");
      //获取foreach标签的close属性,foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
      String close = nodeToHandle.getStringAttribute("close");
      /**
       * 获取foreach标签的separator属性,元素之间的分隔符,例如在in()的时候,
       * separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选
       */
      String separator = nodeToHandle.getStringAttribute("separator");
      ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
      targetContents.add(forEachSqlNode);
    }
  }

  /**
   * if标签接收处理器
   */
  private class IfHandler implements NodeHandler {
    public IfHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      //解析if标签下的子节点,封装成MixedSqlNode
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      //获取if标签的test属性,ognl表达式
      String test = nodeToHandle.getStringAttribute("test");
      IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
      targetContents.add(ifSqlNode);
    }
  }

  /**
   * otherwise标签,当 when 元素中的条件都不生效,就可以使用 otherwise 元素的默认文本。
   */
  private class OtherwiseHandler implements NodeHandler {
    public OtherwiseHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      //解析otherwise标签下的子节点,封装成MixedSqlNode
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      /**
       * 因为otherwise标签的应用情况是由choose标签控制,而且应用otherwise标签不过是
       * 应用otherwise标签的所有子节点对应sqlNode,所有otherwise不需要对应的sqlNode
       */
      targetContents.add(mixedSqlNode);
    }
  }

  /**
   * choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。
   * 当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。
   * 类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
   */
  private class ChooseHandler implements NodeHandler {
    public ChooseHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      List<SqlNode> whenSqlNodes = new ArrayList<>();
      List<SqlNode> otherwiseSqlNodes = new ArrayList<>();
      //接收处理when标签和otherwise标签
      handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
      //获取otherwise标签
      SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
      ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
      targetContents.add(chooseSqlNode);
    }

    /**
     * 接收处理when标签和otherwise标签
     * @param chooseSqlNode choose标签
     * @param ifSqlNodes when标签集合
     * @param defaultSqlNodes otherwise标签集合
     */
    private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
      List<XNode> children = chooseSqlNode.getChildren();
      for (XNode child : children) {
        String nodeName = child.getNode().getNodeName();
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        if (handler instanceof IfHandler) {//when标签的处理器是IfHandler
          //handleNode方法会将child添加到ifSqlNodes集合中
          handler.handleNode(child, ifSqlNodes);
        } else if (handler instanceof OtherwiseHandler) {//Otherwise标签
          //handleNode方法会将child添加到defaultSqlNodes集合中
          handler.handleNode(child, defaultSqlNodes);
        }
      }
    }

    /**
     * 获取otherwise标签
     * @param defaultSqlNodes otherwise标签集合
     * @return 返回集合的第一个元素, {@code defaultSqlNodes} 只能存在一个元素或者没有元素,
     *      超过一个都会抛出BuilderException
     */
    private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
      SqlNode defaultSqlNode = null;
      if (defaultSqlNodes.size() == 1) {
        defaultSqlNode = defaultSqlNodes.get(0);
      } else if (defaultSqlNodes.size() > 1) {
        throw new BuilderException("Too many default (otherwise) elements in choose statement.");
      }
      return defaultSqlNode;
    }
  }

}

RawLanguageDriver

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.defaults;

import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
 * 这是一个简单的语言驱动,只能针对静态SQL的处里,如果出现动态SQL标签会抛出异常
 * As of 3.2.4 the default XML language is able to identify static statements
 * and create a {@link RawSqlSource}. So there is no need to use RAW unless you
 * want to make sure that there is not any dynamic tag for any reason.
 * 默认XML语言是能够识别静态语句并创建一个{@link RawSqlSource},所以没有需要使用原始的,除非你想确保没有任何动态标记任何理由。
 * @since 3.2.0
 * @author Eduardo Macarron
 */
public class RawLanguageDriver extends XMLLanguageDriver {

  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    SqlSource source = super.createSqlSource(configuration, script, parameterType);
    checkIsNotDynamic(source);
    return source;
  }

  @Override
  public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
    SqlSource source = super.createSqlSource(configuration, script, parameterType);
    checkIsNotDynamic(source);
    return source;
  }

  private void checkIsNotDynamic(SqlSource source) {
    if (!RawSqlSource.class.equals(source.getClass())) {
      throw new BuilderException("Dynamic content is not allowed when using RAW language");
    }
  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容