如何在Doobie中动态插值可变长度列表至SQL VALUES子句?
在Doobie中动态生成可变长度的VALUES子句
要处理可变长度列表插入到VALUES子句的需求,Doobie的Fragment类型是你的好帮手——它能安全地动态构建SQL片段,完全不用担心SQL注入问题。下面是具体的实现方案:
1. 编写动态生成VALUES片段的辅助方法
我们可以把列表中的每个元素转换成对应的SQL占位符片段,再拼接成完整的VALUES子句:
import doobie._ import doobie.implicits._ // 把任意List[A]转换成VALUES(?, ?, ...)的Fragment,A需要有对应的Put实例 def buildValuesFragment[A: Put](ids: List[A]): Fragment = { // 将每个元素转为(?)形式的Fragment val valueFragments = ids.map(id => fr"($id)") // 用逗号拼接所有片段,再包裹在VALUES中 fr"VALUES(${valueFragments.intercalate(fr", ")})" }
这里的Put类型类是Doobie用来处理参数绑定的,基本类型(Int、Long、String等)都已经内置了对应的实例,自定义类型需要你自己实现Put。
2. 拼接完整的UNION查询
接下来把生成的VALUES片段和你的主查询拼接起来,注意给子查询指定别名,同时处理空列表的边界情况:
def buildUnionQuery[A: Put](ids: List[A]): Query0[Long] = { ids match { case Nil => // 列表为空时直接查询原表,避免生成无效的VALUES子句 fr"SELECT id FROM table".query[Long] case nonEmptyIds => // 生成VALUES子句并指定别名为table(id) val subQuery = buildValuesFragment(nonEmptyIds).into("table(id)") // 拼接完整的UNION查询 val fullQuery = fr"SELECT id FROM $subQuery UNION SELECT id FROM table" fullQuery.query[Long] } }
3. 使用示例
调用这个方法时,传入你的可变长度列表即可:
// 比如传入List(1,2,3,4) val query = buildUnionQuery(List(1,2,3,4)) // 执行查询(这里假设你已经有Transactor实例xa) val result: IO[List[Long]] = query.to[List].transact(xa)
Doobie会自动帮你把列表中的元素绑定到SQL参数中,最终生成的SQL结构会是:
(SELECT id FROM (VALUES(?), (?), (?), (?)) table(id)) UNION SELECT id FROM table
为什么这样做安全?
不同于直接字符串拼接,Doobie的Fragment会把参数和SQL结构分开处理,所有的参数都会通过预编译语句绑定,完全避免了SQL注入的风险,这也是Doobie推荐的动态SQL构建方式。
内容的提问来源于stack exchange,提问作者WeiChing 林煒清
相关产品推荐
相关产品推荐

