Mybatis的「if」 标签有坑之参数是0的时候会被过滤掉!

您所在的位置:网站首页 x为无法识别的参数 Mybatis的「if」 标签有坑之参数是0的时候会被过滤掉!

Mybatis的「if」 标签有坑之参数是0的时候会被过滤掉!

2024-07-12 11:34| 来源: 网络整理| 查看: 265

前言:

米娜,今天的文章还是简确用的文章,希望可以帮到你们。

Mybatis 有一些标签,用来支持动态 sql 语句,简单来说,这些标签可以控制 sql 语句的输出,设置某些条件来让Mapper输出不同的 sql 语句,今天这篇文章主要说一下使用标签会遇到的坑。

正文:

一、复现问题

1.数据库的数据

2.Controller层代码

@RestController @RequestMapping("/study") public class StudentController { @Autowired StudentService studentService; @RequestMapping(value = "/selectStudent", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") public String selectStudent(Integer sex){ JSONObject result = new JSONObject(); List students = studentService.selectStudent(sex); result.put("data",students); return result.toString(); } }

3.Dao层代码

public interface StudentMapper extends BaseMapper { List selectBySex(@Param("sex") Integer sex); }

4.xml文件的代码

select * from student where 1=1 and sex = #{sex}

5.postman请求的参数

根据上面的代码和数据库已有的数据,我们猜测sex传1的时候,应该有一条数据,sex传0的时候有两条数据,不传的时候有三条数据。我们现在看下结果:

sex=1 ,和我们猜想的一样,1条数据

sex=0 ,出现了三条数据和我们的猜想不一样,问题复现出来了,这时候出现三条数据,说明标签没有起作用

sex传null的时候,和我们猜想的一样,3条数据

二、分析问题

1.我们先看下控制台打印sql的区别

sex=1

select * from student where 1=1 and sex = ?

sex=null

select * from student where 1=1 

sex=0

select * from student where 1=1 

可以明显看出来参数传0的时候,没有走标签里的条件,test判断的似乎把等于0的情况,也当做false,所以没有走if里面的部分。

and sex = #{sex}

2.那为什么会造成sex=0 返回的是false,这得稍微看下MyBatis中一些关于动态SQL的接口和类,先看一张类图

我们可以看到关于if的标签设计到的类是IfSqlNode,那我们就先看下这个类

public class IfSqlNode implements SqlNode { private final ExpressionEvaluator evaluator; private final String test; private final SqlNode contents; public IfSqlNode(SqlNode contents, String test) { this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator(); } public boolean apply(DynamicContext context) { if (this.evaluator.evaluateBoolean(this.test, context.getBindings())) { this.contents.apply(context); return true; } else { return false; } } }

我们发现IfSqlNode的apply方法这里进行了判断,于是推测是否在这里进行条件的判断,我们再进一步看下evaluateBoolean方法

public boolean evaluateBoolean(String expression, Object parameterObject) { Object value = OgnlCache.getValue(expression, parameterObject); if (value instanceof Boolean) { return (Boolean)value; } else if (value instanceof Number) { return (new BigDecimal(String.valueOf(value))).compareTo(BigDecimal.ZERO) != 0; } else { return value != null; } }

通过断点发现value的时候就是false啦,所以看下getValue方法

然后在不断往下追溯源码后,找到方法compareWithConversion,通过断点我们可以到,0和“”最终都会转化成0.0,所以在mybatis的if标签里0其实等价于“”,这也是为什么mybati中if test 0!=""判定为false的原因。

三、解决问题

1.在建表的时候如果某个字段定义成int类型,建议可以用1为基础往上加,这样就可以避免使用0来当参数筛选条件了,从根源上制止。

举个例子,sex可以用1表示男  2表示女 3表示未知

2.如果任性就是想用0来表示,可以在标签这样写  or sex==0

select * from student where 1=1 and sex = #{sex}

然后点击postman测试一下,数据变成两条了

 再看下打印的日志:

=>  Preparing: select * from student where 1=1 and sex = ?  ==> Parameters: 0(Integer)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3