MyBatis源码解析——动态SQL实现原理_mybatis注解动态sql provider(2)

如上面的代码所示,StaticSqlSource类的内容比较简单,只封装了Mapper解析后的SQL内容和Mapper参数映射信息。

LanguageDriver详解

实际上,SQL配置信息到SqlSource对象的转换是由LanguageDriver组件来完成的。下面来看一下LanguageDriver接口的定义,代码如下:

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.
   *
   * @author Frank D. Martinez [mnesarco]
   * @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.
   * @return the parameter handler
   * @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.
   *
   * @param configuration The MyBatis configuration
   * @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.
   * @return the sql source
   */
  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.
   *
   * @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.
   * @return the sql source
   */
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);

}

如上面的代码所示,LanguageDriver接口中一共有3个方法,其中createParameterHandler()方法用于创建ParameterHandler对象,另外还有两个重载的createSqlSource()方法,这两个重载的方法用于创建SqlSource对象。MyBatis中为LanguageDriver接口提供了两个实现类,分别为XMLLanguageDriver和RawLanguageDriver。XMLLanguageDriver为XML语言驱动,为MyBatis提供了通过XML标签(我们常用的、等标签)结合OGNL表达式语法实现动态SQL的功能。而RawLanguageDriver表示仅支持静态SQL配置,不支持动态SQL功能。

接下来我们重点了解一下XMLLanguageDriver实现类的内容,代码如下:

public class XMLLanguageDriver implements LanguageDriver {

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

  // 该方法用于解析XML文件中配置的SQL信息
  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    // 创建XMLScriptBuilder对象
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    
    // 调用XMLScriptBuilder的parseScriptNode()方法解析SQL资源
    return builder.parseScriptNode();
  }

  // 该方法用于解析XML文件中配置的SQL信息
  @Override
  public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
    // 若字符串以<script>标签开头,则以XML方式解析
    if (script.startsWith("<script>")) {
      XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
      return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
    } else {
      // 解析SQL配置中的全局变量
      script = PropertyParser.parse(script, configuration.getVariables());
      TextSqlNode textSqlNode = new TextSqlNode(script);

      // 如果SQL中仍包含${}参数占位符,则返回DynamicSqlSource实例,否则返回RawSqlSource
      if (textSqlNode.isDynamic()) {
        return new DynamicSqlSource(configuration, textSqlNode);
      } else {
        return new RawSqlSource(configuration, script, parameterType);
      }
    }
  }

}

如上面的代码所示,XMLLanguageDriver类实现了LanguageDriver接口中两个重载的createSqlSource()方法,分别用于处理XML文件和Java注解中配置的SQL信息,将SQL配置转换为SqlSource对象。

  • 第一个重载的createSqlSource()方法用于处理XML文件中配置的SQL信息,该方法中创建了一个XMLScriptBuilder对象,然后调用XMLScriptBuilder对象的parseScriptNode()方法将SQL资源转换为SqlSource对象。
  • 第二个重载的createSqlSource()方法用于处理Java注解中配置的SQL信息,该方法中首先判断SQL配置是否以

从XMLLanguageDriver类的createSqlSource()方法的实现来看,我们除了可以通过XML配置文件结合OGNL表达式配置动态SQL外,还可以通过Java注解的方式配置,只需要注解中的内容加上

MyBatis从3.2版本开始支持可插拔脚本语言,这允许我们插入一种脚本语言驱动,并基于这种语言来编写动态SQL语句。例如,我们可以让MyBatis的Mapper配置支持Velocity(或者Freemaker)语法,并基于Velocity(或者Freemaker)语法编写动态SQL。

要实现自定义的脚本语言驱动,只需要实现LanguageDriver接口,创建自定义的SqlSource对象,然后对SqlSource对象进行解析,生成最终的BoundSql对象即可。有兴趣的读者可以参考velocity-scripting模块的源码,该模块为MyBatis的Mapper配置提供Velocity语法支持。

接下来笔者就以velocity-scripting模块为例介绍自定义LanguageDriver的使用。要使用velocity-scripting模块,首先需要在项目中添加该模块的依赖,如果是Maven项目,则只需要在pom.xml文件中增加如下内容:

为了简化LanguageDriver的类型限定名,便于在使用时引用,我们可以在MyBatis主配置文件中为velocity-scripting模块自定义的LanguageDriver指定一个别名,代码如下:

接下来就可以在配置Mapper时使用Velocity语法了,例如:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值