Mybatis源码研究之SqlSource

Mybatis中少有的有注释的接口

1. SqlSource接口

/**
 * Represents the content of a mapped statement read from an XML file or an annotation. 
 * It creates the SQL that will be passed to the database out of the input parameter received from the user.
 * 
 * @author Clinton Begin
 */
 // 以上注释就不作翻译了.
public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

2. 继承图

SqlSource
	StaticSqlSource  //  SqlSource实例的直接构造者就是 SqlSourceBuilder, 而SqlSourceBuilder中的parse方法所返回的是StaticSqlSource实例.
	RawSqlSource  // 
	ProviderSqlSource
	DynamicSqlSource
  1. Mybatis中非常重要的MappedStatement类中就有一个sqlSource字段.

  2. SqlSource实例的直接构造者就是 SqlSourceBuilder, 而SqlSourceBuilder所返回的则是StaticSqlSource实例(SqlSource的实现者之一). 而SqlSource接口的其他三个实现者(RawSqlSource,DynamicSqlSource,ProviderSqlSource)无一例外地都重度依赖于SqlSourceBuilder.

  3. SqlSourceBuilder类也继承了BaseBuilder, 相比较继承自BaseBuilder的其他XMLXxx来说, 这个类在语义上相当突兀. XMLXxxx是我们读Mybatis源码时最先会接触到的代码, 它们就是负责解析配置文件的. 所以我们会顺理成章地猜测继承BaseBuilder来解析XML配置文件. 而SqlSourceBuilder是为了复用BaseBuilder的功能.

  4. DynamicSqlSource 里的主要逻辑, 参见 MyBatis源码研究之$和# 中的补充说明部分.

3. BoundSql接口

关于这个请参见本人的另外一篇博客:Mybatis源码研究之BoundSql

4. LanguageDriver接口

  1. 该接口也是构造返回 SqlSource 的方法.
  2. 而且其子类XMLLanguageDriver(这个类也是Mybatis默认使用的LanguageDriver实现者)中有个细节就是,其createSqlSource方法中的
    script = PropertyParser.parse(script, configuration.getVariables());
    

这样一行代表着 如果编写的映射语句中如果有 ${ } 形式 会使用 configuration.getVariables() 中定义的变量预替换一次, 而且查看PropertyParser类中的内部子类VariableTokenHandler, 它只会换已有变量,对于没有的, 它会原样返回 : return "${" + content + "}";

5. SqlSourceBuilder

  1. 该类的确切位置是在org.apache.ibatis.builder.SqlSourceBuilder
  2. org.apache.ibatis作为Mybatis的根package,我们不需要关心这个.
  3. 作为Mybatis的顶级package之一的builder 主要负责的是对xml配置文件的解析工作,而作为其中一员的SqlSourceBuilder自然不能免俗。
    1. 首先看名字就知道它关联到了SqlSource接口;而这个SqlSource接口的唯一方法返回的是BoundSql
    2. 其次这个类唯一的public方法parse,一眼看上去就是根据程序员提供的原始的sql语句,以及其他几个参数; 来构建出最终的SqlSource.
  4. 在查找对其parse的调用时,我们可以看到DynamicSqlSource类中有对其的使用,而DynamicSqlSource正好就是实现了SqlSource接口.
    DynamicSqlSource实现的getBoundSql方法中,我们可以看到对于在Mybatis的xml书写时,为啥parameterType的内容被无视,就是因为其对parameter type的选择最终还是基于实际的parameterObject的实际类型.
  5. SqlSourceBuilder的内部类ParameterMappingTokenHandler的私有方法buildParameterMapping中我们可以看到
    1. 看名字就知道这个方法是用来构建一个ParameterMapping实例.而ParameterMapping类就是封装了程序员所书写的#{ }中的所有内容.
    2. #{ }中各种参数的相应处理
    3. 目前的可用的参数如下javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName
    4. 例子如下: #{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
    5. 对于这一部分,可以看看MyBatis官方文档中的 >>Mapper XML文件 >> Parameters小节.
    6. 所以可以猜测到: 这些属性只能在#{ },而不能在${ }中使用.

6. ParameterMapping

  1. 虽然在xml映射文件编写时不再推荐使用ParameterMapping. 但在ParameterMappingTokenHandler 的实现里, 依然会将每个 #{ } 里的内容解析为一个ParameterMapping 实例.
  2. 这里补充一句: 每个ParameterMapping 实例真正被使用的位置则是位于ParameterHandler接口唯一的实现者DefaultParameterHandler 中的setParameters方法中. 所以我们在使用JDBC编程时进行的PreparedStatement.setXXX 操作其实是在每个 TypeHandler 接口的实现类中完成的.
//TypeHandler的使用位置: `DefaultParameterHandler`类中的第77行 (Mybatis-3.2.2.jar)
typeHandler.setParameter(ps, i + 1, value, jdbcType);  

7. ParameterExpression

SqlSourceBuilder的内部类ParameterMappingTokenHandler中,其parseParameterMapping方法中对ParameterExpression类的使用

  1. 这个ParameterExpression类封装了对#{ }中内容的解析逻辑,
  2. 而且其继承自HashMap,所以可以将解析的结果以键值对的方法存储起来.
  3. 外界对其的调用直接以Map的方式获取即可.
  4. 所以在使用#{ }中的内容得到一个ParameterExpression实例之后, 就能直接使用Map接口提供的方法调取用户在#{ }设置的属性值了.

8. Links

  1. Mybatis中几个重要类

你可能感兴趣的:(MyBatis3,mybatis,源码)