MyBatis源码的学习(14)---SqlSource和SqlNode

MyBatis源码的学习(14)---SqlSource和SqlNode_第1张图片

MyBatis源码的学习(14)---SqlSource和SqlNode_第2张图片

sqlSource接口中最底层的,最基础的是: StaticSqlSource,里面sql字段用于存放解析好的sql,比如将'#{}'替换为“?”占位符

BoundSql getBoundSql(Object parameterObject);

RawSqlSource--.>StaticSqlSource-- >sql

常规的解析Mapper.xml的时候,我们创建SqlSource对象:

XMLStatementBuilder.parseStatementNode()

MyBatis源码的学习(14)---SqlSource和SqlNode_第3张图片

//XMLScriptBuilder类
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如果不存在动态标签,'${}',也就是isDynamic值为false时,我们创建的是一个RawSqlSource类型的对象,RawSqlSource中有一个SqlSource属性,是一个StaticSqlSource类型的对象,直接就会将'#{}'替换为‘?’占位符。这种类型的

SqlSource在getBoundSql()的时候,直接将StaticSqlSource.getBoundSql()返回,不需要进行parse操作。parse操作发生在我们的new RawSqlSource()这一步。

//RawSqlSource类
@Override
  public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
  }

//StaticSqlSource类
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return new BoundSql(configuration, sql, parameterMappings, parameterObject);
  }

MyBatis源码的学习(14)---SqlSource和SqlNode_第4张图片

对于,动态的sqlSource,DynamicSqlSource,我们只是直接创建了对象,并没有做sql的解析。sql的解析工作发生在,我们调用getBoundSql()的时候。

MyBatis源码的学习(14)---SqlSource和SqlNode_第5张图片

我们的sqlNode接口,使用了组合模式,用于构建一个树结构的数据。

MixedSqlNode混合节点,它下面可能还有其他节点,它的所有枝叶子节点都放到list中,我们拼接sql时候,遍历节点,然后组装就可以了。

public class MixedSqlNode implements SqlNode {
  private final List contents;

  public MixedSqlNode(List contents) {
    this.contents = contents;
  }

  @Override
  public boolean apply(DynamicContext context) {
   //DynamicContext  里面有个stringBuild对象用来拼接sql,最终生成一个完整的sql(将动态标签,'${}'都处理好)
    contents.forEach(node -> node.apply(context));
    return true;
  }
}
我们的根节点就是MixedSqlNode这个节点,它下面可以有很多平级的sqlNode,然后这些平级的sqlNode最终调用apply()方法,会一直解析下去,
最终我们最底层的sqlNode都是StaticTextSqlNode节点。
 

就比如,我们解析一对。。。标签时,会生成一个ForEachSqlNode ,调用apply方法时,会生成具体的sql

片段。比如:

insert into u_user(usercode,username,createtime,usertype,mobile)   

      (#{user.userCode},#{user.userName}
      ,#{user.createTime},#{user.userType},#{user.mobile})
    
解析后:
   
    insert into u_user(usercode,username,createtime,usertype,mobile)
    values
      
      (#{__frch_user_0.userCode},#{__frch_user_0.userName}
      ,#{__frch_user_0.createTime},#{__frch_user_0.userType},#{__frch_user_0.mobile})
     , 
      (#{__frch_user_1.userCode},#{__frch_user_1.userName}
      ,#{__frch_user_1.createTime},#{__frch_user_1.userType},#{__frch_user_1.mobile})
     , 
      (#{__frch_user_2.userCode},#{__frch_user_2.userName}
      ,#{__frch_user_2.createTime},#{__frch_user_2.userType},#{__frch_user_2.mobile})

MyBatis源码的学习(14)---SqlSource和SqlNode_第6张图片

我们再来看看,一般的文本节点:

TextSqlNode('${}') ,StaticTextSqlNode('#{}')

在我们的TextSqlNode中,处理'${}'的一共有俩种TokenHandler:

DynamicCheckerTokenParser(只进行判断sql中是否有 '${}'存在,如果存在,最终生成一个DynamicSqlSource), BindingTokenParser(在我们调用getBoundSql的时候,会将'${}'进行赋值操作。select * from ${tablename} 变为
select * from user)

另一种文本节点类:StaticTextSqlNode是最最基础的节点,它里面直接就是sql片段,可能包含'#{}'。

MyBatis源码的学习(14)---SqlSource和SqlNode_第7张图片

@Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
//处理动态标签和'${}',但是这时候,我们的'#{}'还未被处理
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
//parse方法中,会将'#{}'处理为"?"占位符,同时返回一个StaticSqlSource
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    //返回的就是一个最基础的 StaticSqlSource
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //将实际入参放入map中
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }

总结:对于非动态SQL(无sql标签或"$"),我们在生成sqlSource的时候,就会进行解析,将'#{}'替换为‘?’占位符。等到getBoundSql()的时候,直接返回值就可以了。

对于动态SQL,我们在调用getBoundSql()的时候才会开始做解析,解析完动态标签和'${]'之后,会继续进一步进行'#{}'的替换工作,然后返回一个BoundSql对象。

 

 

 

你可能感兴趣的:(Mybatis源码学习)