MyBatis--sql执行过程(${}和#{}源码解析)

505 阅读1分钟

在上一篇文章我是解析到获取BoundSql,并且最后我是发出了一张BoundSql的结构 image.png

    方法:
    Peopel selectOne(@Param("name")String name, @Param("id")int id);
    配置文件:
    <select id='selectOne'>
       select * from test where 1 = 1 and id = ${id} and name = #{name}
    </select>

调用的方法与配置的sql语句。 在之前我们知道我们在配置文件写的sql语句最后是被解析成一个个的SqlNode,然后将一个个的SqlNode存储到SqlSourse对象中去。不清楚的可以看下我之前的文章。在我主页里面有关于解析MyBatis源码的文章。废话不多说了。继续上次的文章,juejin.cn/post/684490… 截屏2019-11-10上午11.36.28

这个DynamicContext是构建完整Sql语句的上下文。 this.rootSqlNode.apply(context);是将一个个的SqlNode拼接起来,底层也就是用了StringBuilder.append()方法拼接的。关于${}的解析也是在这个方法实现的。在包org.apache.ibatis.scripting.xmltags.TextSqlNode下有这样的一个方法。如下:

就是在解析我们的sql语句的时候如果碰到{}符号就用TextSqlNode来封装,所以在拼接时候也是如此。解析很简单就是首先找到这个SqlNode的{}位置,然后进行字符串替换。所以说{}是字符串替换也就是因为这样。然后获取到{}符号中的值,还记得之前我们在解析的时候方法参数的解析吗??方法参数会被解析好然后放到一个map中。所以此时我们得到${}中的值就是map中的key,通过这个key去拿到值,然后进行字符串替换即可。

解析完${}符号,接下来就是#{}了。

我们进入到这个parse()方法中

跟解析${}一样,但是#{}不是字符串替换,而是用 ? 代替。 我们知道方法参数 public void selectById(@Params("name")String name,@Params("id")int id) 会被ParamNameResolver解析成一个Map,Map中KEY值自然就是注解@Param中的值,VALUE就是我们传进来的值,在解析#{}占位符的时候,每一个#{}占位符都会被解析成一个ParameterMapping对象,ParameterMapping{property='name',mode=IN,javaType=class java.lang.String,jdbcType=null....},当然id也会解析成这个格式,然后将解析好的ParameterMapping存放到一个List中去,具体实现是在 ParameterMappingTokenHandle类中的handleToken方法实现的。我们知道List是个有序集合,所以在将❓替换成我们传的参数就会遍历这个list,然后与我们的参数一一对应起来。整个流程如下图 image.png
循环list得到每个ParameterMapping对象,然后得到该对象的property属性,最后从参数Map中获取到值,之前说过参数经过解析会存储在一个Map中的,在经过这一步之后就能得到一条完整的SQL语句了,select * from test where name = #{name} and id = #{id} 就会解析成 select * from test where name = "bjh" and id = 1。

所以在编写sql配置文件的时候如果使用${},那么如果是字符串类型要加上 '',不然会报错,尽量使用#{},防止sql注入。整个的解析流程就是这样,得到了完整的sql语句那么就执行就可以了。执行下次在写吧。