Slick中编写带%通配符LIKE的原生SQL查询参数越界问题
问题原因
Slick的sql原生SQL插值器会按照SQL语法规则解析插值位置:
- 只有不在SQL字符串字面量(单引号包裹的内容)内部的
$开头变量,才会被识别为待绑定的预编译参数,替换为JDBC的?占位符后按顺序传值 - 单引号内部的
$标记不会被插值器识别,会直接作为普通SQL字符串内容处理,最终会导致插值器统计的参数数量和JDBC驱动识别到的占位符数量不匹配,抛出parameter index out of range异常
硬编码搜索值可以正常运行,是因为此时SQL中不存在任何插值变量,属于完全静态的SQL语句,不会触发参数绑定逻辑,自然不会报错。
注意:手动给插值变量加单引号是错误写法,Slick做预编译参数绑定时,会自动根据参数类型处理引号包裹、特殊字符转义逻辑,不需要手动加单引号。
安全正确的实现方式
将%通配符和搜索内容的拼接逻辑放在Scala代码侧完成,把拼接好的完整LIKE匹配模式作为独立的插值参数传入即可,这种写法走JDBC预编译参数绑定,完全没有SQL注入风险。
val someText: String = "some text" // 在业务代码侧拼接LIKE匹配通配符 val searchPattern = s"%$someText%" val query = sql"""SELECT mt.id, mt.name FROM my_table mt WHERE mt.name LIKE $searchPattern;""".as[MyEntity] val queryResult: Future[Vector[MyEntity]] = db.run(query)
如果需要避免用户输入内容中自带的%、_这类LIKE通配符干扰匹配结果,可以先对输入内容做转义处理:
val someText: String = "some text" // 转义LIKE语法中的特殊字符 val escapedText = someText .replace("\\", "\\\\") .replace("%", "\\%") .replace("_", "\\_") val searchPattern = s"%$escapedText%" val query = sql"""SELECT mt.id, mt.name FROM my_table mt WHERE mt.name LIKE $searchPattern ESCAPE '\';""".as[MyEntity] val queryResult: Future[Vector[MyEntity]] = db.run(query)
内容的提问来源于stack exchange,提问作者rapt
相关产品推荐
相关产品推荐

