You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Doobie拼接Option字段动态查询时IN子句占位符异常问题

问题原因

你的IN子句拼接逻辑存在错误,导致整个List[String]类型的地址列表被Doobie识别为单个绑定参数,最终只生成了一个?占位符。
另外需要明确:Doobie默认生成带?的参数化预处理SQL是正常的安全行为,目的是避免SQL注入,不需要强行把参数值直接拼接进SQL字符串,你只需要保证IN子句内的占位符数量和列表元素数量一致即可,执行时框架会自动完成参数绑定。

正确实现

Doobie已经内置了Fragments.in方法专门处理IN子句,不需要手动实现列表元素展开、逗号拼接逻辑,从根源避免手写错误:

import doobie._
import doobie.implicits._

case class Request(name:Option[String], age: Option[Int], address: Option[List[String]])

object QueryBuilder {
  def build(req: Request): Fragment = {
    val baseSql: Fragment = sql"select name, age, email from user"
    val nameFilter: Option[Fragment] = req.name.map(x => fr"name = $x")
    val ageFilter: Option[Fragment] = req.age.map(x => fr"age = $x")
    // 直接调用内置in方法生成IN子句,自动按列表长度生成对应数量的占位符
    val addressFilter: Option[Fragment] = req.address.map(addrList => 
      Fragments.in(fr"address", addrList)
    )

    baseSql ++ whereAndOpt(nameFilter, ageFilter, addressFilter)
  }
}

传入Request(Some("abc"), Some(27), Some(List("add1", "add2")))时,生成的预处理SQL如下,完全符合预期:

select name, age, email from user WHERE name = ? AND age = ? AND address IN (?, ?)

执行时框架会按顺序绑定四个参数:"abc"、27、"add1"、"add2"。

手动拼接注意事项(不推荐)

如果坚持手动拼接IN子句,需要确保每个列表元素都生成独立的Fragment,再通过intercalate拼接逗号,不要将整个列表作为单个参数插值,同时注意SQL插值器fr必须和后接的字符串双引号紧挨着,否则会出现语法解析错误:

val addressFilter: Option[Fragment] = req.address.map(addrList => {
  val elementFragments = addrList.map(addr => fr"$addr")
  val placeholderPart = elementFragments.intercalate(fr",")
  fr"address IN ($placeholderPart)"
})

你之前手写的版本存在片段拼接逻辑拆分过碎的问题,很容易出现参数误传,生成错误的占位符数量。

内容的提问来源于stack exchange,提问作者Raman Mishra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 19:45:55