Mybatis—LanguageDriver

  LanguageDriver主要用于构造SqlSource和ParameterHandler,从LanguageDriver在Mybatis中调用链路来看,Configuration#newParameterHandler通过调用LanguageDriver#createParameterHandler完成构建ParameterHandler,通过调用LanguageDriver#createSqlSource来完成构建SqlSource。
  LanguageDriver接口一共只定义了createParameterHandler和两个createSqlSource方法,其结构也相当简单,Mybatis只实现了RawLanguageDriver和XMLLanguageDriver两个类,且RawLanguageDriver还继承自XMLLanguageDriver,如下图所示。在这里插入图片描述

  • XMLLanguageDriver,XML语言驱动,主要帮助Mybatis实现通过XML标签描述的动态SQL。
  • RawLanguageDriver,与RawSqlSource一样,主要表示支持静态SQL信息配置。

XMLLanguageDriver

  XMLLanguageDriver以XML打头就已经明确说明该实现主要是为了支持使用XML标签描述的动态SQL,其完整实现了LanguageDriver接口定义的三个方法,其中createParameterHandler方法的实现仅是通过DefaultParameterHandler构造函数构造了一个ParameterHandler,所以就不再做过多的讨论。

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

createSqlSource

  LanguageDriver接口定了两个createSqlSource方法从源代码来看二则唯一的区别就在于入参的第二个参数,一个接口定义所需要的XNode类型的script,另一个接口定义则需要String类型的script。Mybatis之所以这么设计,是因为一个方法是用于处理通过XML配置的SQL信息,另一个方法则用于处理通过注解配置的SQL信息。

  • 处理XML配置的SQL信息

  从源代码来看,用于处理XML配置的SQL信息的createSqlSource方法逻辑相对清晰,

@Override
public SqlSource createSqlSource(
      Configuration configuration, 
      XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(
        configuration, script, parameterType);
    return builder.parseScriptNode();
}
  1. 通过所传入的参数构建一个用于处理XML配置的XMLScriptBuilder对象;
  2. 调用XMLScriptBuilder#parseScriptNode方法将SQL信息转换为SqlSource对象。

  XMLLanguageDriver使用XMLScriptBuilder来构建了SqlSource对象,由此可见XMLScriptBuilder的职责就是解析XML配置信息构建对应的SqlSource对象,如下源代码所示,XMLScriptBuilder的构造函数在完成对属性赋值之后,又通过initNodeHandlerMap方法初始化了一批针对XML配置标签的处理器。之后在调用XMLScriptBuilder#parseScriptNode方法时通过解析结果中的isDynamic来分别构建DynamicSqlSource和RawSqlSource。

public XMLScriptBuilder(
    Configuration configuration, 
    XNode context, Class<?> parameterType) {
    super(configuration);
    this.context = context;
    this.parameterType = parameterType;
    initNodeHandlerMap();
}

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());
}

public SqlSource parseScriptNode() {
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}
  • 处理注解配置的SQL信息

  处理注解配置的SQL信息的createSqlSource方法有两个处理分支,一个处理script文本中有<script>标签的SQL配置信息,另一个处理不带<script>标签的SQL配置信息。处理逻辑详情:

  1. 如果所传入的script文本中包含<script>标签,那么构造一个XPathParser用于将script文本构造成XNode对象,然后调用用于处理XML配置的SQL信息的createSqlSource;

    如,@select(“<script> select * from a <where> <if b != null> and c = b </if> </where> </script>”)

  2. 如果所传入的script文本中不包含<script>标签,那么直接通过判断当前是否是动态SQL配置信息来分别构建DynamicSqlSource和RawSqlSource。


@Override
public SqlSource createSqlSource(
      Configuration configuration, 
      String script, Class<?> parameterType) {
    if (script.startsWith("<script>")) {
      XPathParser parser = new XPathParser(
          script, false, configuration.getVariables(),
          new XMLMapperEntityResolver());
      return createSqlSource(
          configuration, parser.evalNode("/script"), parameterType);
    } else {
      script = PropertyParser.parse(script, configuration.getVariables());
      TextSqlNode textSqlNode = new TextSqlNode(script);
      if (textSqlNode.isDynamic()) {
        return new DynamicSqlSource(configuration, textSqlNode);
      } else {
        return new RawSqlSource(configuration, script, parameterType);
      }
    }
}

RawLanguageDriver

  RawLanguageDriver继承自XMLLanguageDriver,所以它是完全依托于XMLLanguageDriver的实现来完成构建SqlSource。从源代码可见其逻辑只是在XMLLanguageDriver构建SqlSource的基础之上增加了一个是否是动态SQL配置的判断,如果是动态SQL配置,就会直接抛出BuilderException。

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");
    }
  }

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐只乐之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值