写在前面
《Docker+SpringBoot+Mybatis+thymeleaf的Java博客系统开源啦》
由于开源了项目的缘故,很多使用了My Blog项目的朋友遇到问题也都会联系我去解决,有的是把问题留在项目的issue里提出,有的是在我的私人博客里留言,还有的则是直接添加我的qq来找我讲自己遇到的问题,有些问题比较简单直接就解决了,有些问题的解决记录也留在issue记录里,有些则是网上有相关教程,而剩下问题的解决方案,如果时间允许我都会单独的做一篇博客来解答。
问题描述
当时的聊天记录:
截图中提到的代码(节选):
ContentVoMapper.xml:
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
CommentVoExample:
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
...
问题整理:在GeneratedCriteria类中并没有valid这一属性,仅仅只有一个isValid()
方法,但是在Mapper文件中mybatis的<if test>
语法中,却有criteria.valid
的表达式,而且程序可以正常运行,这是怎么回事呢?
思路整理
首先,我刚看到这个问题的时候也是有点懵,因为这个代码其实不是我写的,Mapper文件是我通过Mybatis-Generator自动生成的,所以这段代码我也是有点陌生的,哈哈哈哈。
但是看了一遍代码之后,我觉得应该是mybatis根据valid属性自动找到了isValid()方法,然后执行了逻辑判断,当然,这都是个人感觉,没什么依据,隐隐约约觉得应该是这么个道理。但是呢,毕竟这位朋友是来问问题的,我不能就简简单单的回复这么一句话,而且是连我自己都不确定的答案。
疑惑的问题有:
- 并不知道mybatis是不是这个执行流程;
- 即使是如上的流程,那么为什么根本没有的属性会被mybatis正常解析;
- 为什么mybatis会去执行
isValid()
方法而不去执行其他的方法。
解决过程
带着以上的问题和心中的不确定,我唯一能做的就是去查看这部分过程的源码了,最终也如愿得到了答案,通过IDEA的debug功能得到了代码的执行过程,可以自行执行查看一下整个过程:
- 在
IfSqlNode
类中,获取了if test
标签中表达式的值:criteria.valid
;
- 接着是在
ObjectPropertyAccessor
类中解析到了需要操作的属性值Criteria
类中的valid
;
- 然后是在
OgnlRuntime
类中得到了表达式对应执行的MethodisValid()
方法。
接下来就是执行方法并获取返回值了,就不再截图了。
上面的前两个问题就有了答案:
- 由
<if test="criteria.valid">
到执行isValid()
方法的执行流程找到了,虽然过程较多但是几个重要的节点就是以上三点,获取Mapper表达式中的类名和属性值,然后获取需要执行的方法,最终实现整个功能。 - mybatis并没有去关注是否存在这个属性,而是根据属性去找到对应的方法并执行。
至于第三个问题,我也做了一下扩展,如果其他带有valid字符串的方法会不会也被执行到,结果是肯定回答,如图:
将isValid()
改为getValid()
OgnlRuntime
类中得到了对应执行的MethodgetValid()
方法
当两个方法都存在时,会执行isValid()
方法,因为if test
需要的是一个boolean返回值,当只存在getValid()
方法时,则会执行getValid()
。
结语
首发于我的个人博客。
如果有问题或者有一些好的创意,欢迎给我留言,也感谢向我指出项目中存在问题的朋友,关于这篇文章,特别感谢一下@libinghui
。
代码和这次的问题都是My Blog项目中的,如果你想继续了解该项目可以查看整个系列文章Java开源博客My-Blog(SpringBoot+Docker)系列文章,也可以到我的GitHub仓库或者开源中国代码仓库中查看源码及详细的部署过程和使用文档。