大意了,无用了MyBatis的$符号

复盘

近期有一个小业务需求,非常简单,本质就是增加几个过滤参数,可以透传给数据库就可以了。

代码使用MyBatis作为ORM框架

原始SQL如下


上面没有过滤条件,本次改造是增加client_type的过滤,由于可能存在多个client_type,故需要使用MyBatis的foreach用法构造in条件。当时偷了个懒,直接从现有代码库里面copy了一份做了改动。

第一版改造如下:


关于foreach用法可以参照:https://juejin.cn/post/6844903886575108110

自以为应该没有问题,就屁颠屁颠的部署服务到预发环境了,结果报错了。

MyBatis的异常:

Unknown column 'android' in 'where clause'] with root cause

奇怪,怎么会把android当做了列呢,这明明是查询条件才对。从日志里面又捞取了一下执行的SQL,终于发现端倪,原来传入到的条件并没有添加""。

select device_uuid as deviceUUID, client_type as clientType       
from tb_device
where client_type in  (android , iOS )
order by gmt_modified desc

那就再检查一下代码吧,把自己的代码和原来copy的代码仔细做了一下对比,并未发现异常,奇怪+1。

那就再检查一下对应的字段类型吧,发现原来正常工作的类型是int,所以即使不加引号也可以正常运行。但是这里的client_type是varchar类型,如果不加引号那就会报错。

所以问题来了,为什么没有添加引号呢? 还是检查一下MyBatis的Mapper文件,发现犯了一个巨大的错误,其他条件地方使用的是#+变量名,这里使用了$+变量名,那就替换一下吧。

更新后SQL:


替换以后执行就正常了。

==>  Preparing: select device_uuid as deviceUUID, client_type as clientType from tb_device where client_type in ( ? , ? ) order by gmt_modified desc 
==> Parameters: android(String), iOS(String)
<==      Total: 0

延伸

关于MyBatis #和$的区别,可以参照一下这个https://segmentfault.com/a/1190000004617028

提取一下文章关键信息,mybatis 在对 sql 语句进行预编译之前,会对 sql 进行动态解析。

  • #{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,能使用#{ }的地方就用#{ }
  • ${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换,表名作为变量时,必须使用 ${ }。

你可能感兴趣的:(mybatis)