1. 一个“优雅”的梦想,始于一次惨痛的SQL注入
只要是天天写业务代码的Java后端,几乎没人能绕开MyBatis里的模糊查询。需求往往一模一样:用户输入几个关键字,我们在数据库里做 LIKE '%关键字%' 的匹配,把符合条件的数据拉出来。标题写的是“mybatis中like优雅使用”,我理解这里的“优雅”,不是指代码写得花里胡哨,而是指安全、可维护、不给自己埋雷。但这三个词,听起来容易,做起来经常是另一回事。
我见过太多团队在模糊查询上翻车。最典型的就是图省事,在Mapper XML里直接写:
xml复制<select id="listByKeyword" resultType="com.example.User">
SELECT * FROM user
WHERE name LIKE '%${keyword}%'
</select>
第一眼看过去,这SQL好像没什么问题,跑起来也通。但稍微有点经验的开发,看到 ${keyword} 这几个字符,血压就应该上来了。${} 是字符串替换,MyBatis会把传入的值原封不动地拼进SQL语句里。也就是说,用户如果在搜索框输入 ' OR '1'='1,最终执行的就是:
sql复制SELECT * FROM user WHERE name LIKE '%' OR '1'='1%'
这条SQL在大多数数据库里都能把整个表的数据带出来。如果后面再接上 UNION SELECT 之类的语句,数据泄露就是分分钟的事。这个坑不是危言耸听,OWASP的SQL注入攻击案例里,模糊查询的注入比例相当高,因为很多人觉得“不过是个搜索框而已”。
所以当我看到有人问“MyBatis里Like怎么优雅使用”时,我第一反应是:先把 ${} 从代码里删掉,再来谈优雅。这不是技术选型问题,是安全底线问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种安全的Like写法:从CONCAT到bind标签
2.1 写法一:XML里用CONCAT函数拼通配符
使用 #{} 是MyBatis推荐的方式,它会被解析成预编译的占位符 ?,配合JDBC的 PreparedStatement,用户输入的任何内容都只是“数据”,不会变成“SQL逻辑”。这是安全的根本保证。
但 #{} 有个限制:你不能直接在占位符里写 %,也就是 LIKE '%#{keyword}%' 这种写法在MySQL里是语法错误。于是最常见的解决思路,就是用数据库自带的字符串拼接函数:
xml复制<select id="listByKeyword" resultType="com.example.User">
SELECT * FROM user
WHERE name LIKE CONCAT('%', #{keyword}, '%')
</select>
这个方法在MySQL里很好用,CONCAT 把通配符和参数拼成一个完整的匹配串,#{} 负责安全绑定参数。代码简短,语义清晰,大多数人误以为这就是“优雅”的终点。
2.2 写法二:Java层拼好通配符再传入
另一个思路是,把 % 的拼接从数据库层拿到Java层。在Service或Controller里,把用户输入处理成带通配符的字符串,Mapper接口里只接收一个普通参数:
java复制public List<User> searchUsers(String keyword) {
// 这里可以顺手去掉首尾空格,也可以对特殊字符做预处理
String pattern = "%" + key
